【SwiftUI】@Stateの使い方

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

スポンサーリンク

環境

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

【Xcode】11.2.1
【Swift】5.1
【iOS】13.2.2
【macOS】Catalina バージョン 10.15.2
スポンサーリンク

@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内のみで使うデータフローを扱う事に適している仕組みです。

スポンサーリンク

子Viewに値を渡す

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

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

子View側では、@Stateではなく@Bindingでプロパティを宣言します。
自らは保有せずに、親のプロパティを参照する事を示します。
これにより、子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が別に用意されています。

スポンサーリンク

あわせて読みたい記事

【SwiftUI】@ObservedObjectの使い方
データクラスの更新を監視する@ObservedObjectの使い方を解説します。これを利用すると複数のViewから同一インスタンスへの同期が可能になります。
Kindle unlimited 読み放題で読める管理人のおすすめ本です!
スポンサーリンク
SwiftUI
カピ通信