前回で、Viewの階層に渡って、同じ変数を参照でき、更新が通知される@Bindingをチェックしました。
でも、@Bindingは、自分の子Viewにその度に渡さなければいけなくて、すこし面倒でした。
@EnvironmentObject
ビュー階層とは、別に存在して、参照することができるようにする仕組みです。
逆に、@Stateの変数は、SwiftUIがそのライフサイクルを管理してくれていましたが、@EnvironmentObjectは、自分で管理する必要があります。
@Published
@EnvironmentObjectで扱うクラスを自分で作る必要があるのですが、そのクラスの中で、監視対象となるべきプロパティには、”@Published”を属性として設定する必要があります。イメージとしては、@State属性をつける変数を自分で定義するクラスで保持するとすると、そのプロパティ(変数)に@Publishedとつける感じになります。
サンプル
自分で、ライフサイクルを管理する関係で、ContentViewを作成する段階で、自分でクラスをインスタンス化する必要があります。
コードを順番に載せますね
以下は、ViewModelです。このクラスが、それぞれのビューが表示したり入力したりするデータを保持するという想定です。
class MyViewModel:ObservableObject { @Published var count:Int = 0 }
ContentViewを作成する段階でMyViewModelをインスタンス化し、EnvironmentObjectとして登録します。
# SceneDelegate#Scene中で、インスタンス化して、登録する必要があります。
Let contentView = ContentView() の行を以下のように変更します。
let myViewModel = MyViewModel() let contentView = ContentView().environmentObject(myViewModel)
これで、インスタンス化したMyViewModelが、EnvironmentObjectとして参照できるようになります。
参照の方法は、以下のコードです。
struct ContentView: View { @EnvironmentObject var myViewModel: MyViewModel var body: some View { HStack() { Button.init("-", action: { self.myViewModel.count = self.myViewModel.count - 1 }) SubView() Button.init("+", action: { self.myViewModel.count = self.myViewModel.count + 1 }) } } } struct SubView:View { @EnvironmentObject var myViewModel: MyViewModel var body: some View { VStack { Text("\(myViewModel.count)") } } }
ポイントは、以下となります。
- @EnvironmentObject属性をつけて変数を定義する。
- どのビューからも参照できるので、ビュー間で受け渡ししなくて良い
まとめ
ここまでで、@State, @Binding, @EnvironmentObjectを見てきました。
複雑なアプリケーションを考え始めると、モデルを外部に持つことが多くなるでしょうから、@EnvironmentObjectを使うことが多くなるかと思います。
UIKitのときにも、AppDelegateに持たせて、受け取ったりしていたと思いますが、変数を属性をつけて宣言することで、受け取れてしまうのですから、すごいですよね。
アプリケーションのロジックや操作性に集中できる気がします。
本当に基本的な要素を説明したと思いますので、SwiftUI 振る舞い基礎編は、ここまでです。
Sponsor Link