【SwiftUI】@Stateの使い方

(2021/05/22 更新)

SwiftUIのデータバインディングの仕組みの一つである、@Stateについて解説します。
プロパティが更新された場合に、参照しているViewも同時に更新される仕組みが実現できます。

その他のデータバインディングについてはこちらの記事を御覧ください。

【SwiftUI】データバインディング
SwiftUIのデータバインディングの仕組みである@State、@ObservedObject、@EnvironmentObject各々の特徴について解説します。

スポンサーリンク

環境

この記事の情報は次のバージョンで動作確認しています。

【Xcode】12.5RC
【Swift】5.4
【iOS】14.5
【macOS】Big Sur バージョン 11.1
スポンサーリンク

@Stateの概要

@Stateはプロパティの宣言時に使えるSwiftUIのカスタム属性です。
プロパティの値とUIの状態を自動的に同期する仕組みを実現します。

@Stateをつけたプロパティには次の2つの機能が付加されます。

  1. 値が更新可能になる。
    SwiftUIのViewはStructの為、通常ではプロパティを更新できませんが、@Stateを付けると更新可能になります。
  2. 値の変更がSwiftUIよってモニタリングされる。
    モニタリングしたプロパティに変更があった場合、対象プロパティを参照しているViewが自動的に再描画されます。これにより、プロパティの値とUIの状態の同期が実現されます。

サンプルソース

isPowerOnプロパティが@Stateで宣言されており、電源ボタンのクリックで値のtrue/falseが切り替わります。
isPowerOnプロパティが更新されると、それに合わせてテキストが自動的に再描画されます。

【図1:サンプル】

スポンサーリンク

@State使用時の注意点

@Stateで宣言されたプロパティはそれを保持するViewと、そのプロパティを参照する配下のViewからしかアクセスできません。この為、private修飾子の使用をAppleでは推奨しています。

外からは値を設定できない為、プロパティは初期値が必要になります。
View内のみで使うデータフローを扱うのに適している仕組みです。

イニシャライザで値を設定する場合

@Stateプロパティは、イニシャライザで値を設定する場合、書き方に特徴があります。

次のようにそのまま設定しても、無視されます。

正しく値を設定したい場合は、次のようにします。

  1. 対象プロパティ名の頭に"_"を付加します。
  2. 設定する値を State(initialValue: 値) の表記にます。

スポンサーリンク

子Viewに値を渡す

先程のサンプルソースの電源ボタンの部分をカスタムView化して分割した例を示します。

@Stateで宣言されたプロパティを子Viewに渡す時は、プロパティ名の頭に$を付けます。
プロパティの値そのものではなく、プロパティへの参照を渡すイメージです。

子View側では、@Stateではなく参照を示す@Bindingでプロパティを宣言します。
自らは値を保有せずに、親のプロパティへの参照を表します。
これにより、子View内でのプロパティ更新は、親のプロパティに反映(=Viewにも反映)されます。

同様の方法で、子Viewのさらに子Viewへと親プロパティの参照値を受け継げます。

子Viewのイニシャライザで受け取る場合

子View側のイニシャライザで@Bindingプロパティに値を設定する場合、書き方に特徴があります。

注意点が2点

  1. 引数の型は、"Binding<型> "と表します。
  2. インスタンスプロパティ名の頭に"_"を付加して記述します。

インスタンスプロパティ名に"_"を付けずに、self.isPowerOnとすると、次のようなコンパイルエラーになります。

標準部品に値を渡す

ToggleやTextFieldのような標準部品でも、この仕組みを利用しています。

例えば、以下はToggleのイニシャライザです。

引数isOnの型がBinding<Bool>になっているのがわかります。
先程のサンプルの子View呼び出しの部分を次のコードに置き換えるだけで、電源ボタンの代わりにToggleボタンが使用できます。

【図2:Toggleに置き換え】

スポンサーリンク

構造体とクラスについて

値型である構造体に対しては、他の一般的な型と同様に@Stateが使用可能です。
しかし参照型であるクラスに対しては、うまく動きません。SwiftUIがクラスの値変更を感知できず、Viewが再描画されない為です。
データクラスの変更を監視してViewと同期をとる仕組みとしては@ObservedObjectが別に用意されています。

スポンサーリンク

参考リンク

イニシャライザで@Stateプロパティの値を設定する方法については、こちらの記事を参考にさせてもらいました。

@State付きの変数の値の初期化が無効になる
以下のコードは、words1という配列がinit内で値が代入され、その後for文を使い、配列words2へ同じ要素を追加するものです。 ですが、words2に@Stateが付いているとwords2.countのように要素の数を調べた時に0と...
スポンサーリンク

あわせて読みたい記事

【SwiftUI】データバインディング
SwiftUIのデータバインディングの仕組みである@State、@ObservedObject、@EnvironmentObject各々の特徴について解説します。
【SwiftUI】@ObservedObjectの使い方
(2020/11/28 更新) SwiftUIのデータバインディングの仕組みの一つで、データクラスの更新を監視する@ObservedObjectの使い方を解説します。 これを利用すると複数のViewから同一インスタンスへの同期が可能になりま...
【SwiftUI】@EnvironmentObjectの使い方
SwiftUIのデータバインディングの仕組みの一つで、アプリケーション全体でデータを共有する為の@EnvironmentObjectの使い方を解説します。 その他のデータバインディングについてはこちらの記事を御覧ください。