IOS 13のStateObjectの代替


の追加StateObject IOSの14では素晴らしいです、それは簡単に任意のグローバルオブジェクトを使用することなくビューモデル(またはコントローラまたは任意の名前を表示するには、ビューを管理するコードのステイタリーな部分に)を接続することができます.
前に、そして今でも、私は多くの例を参照してくださいObservableObject が作成されます.
// Don't do this!!
@ObservedObject var foo = Foo()
問題は、新しいインスタンスがFoo が含まれているビューが作成されるたびに作成されます.そして、私はスクリーンでその見解の外観について話していません.Swiftui再表示するたびにビューの構造体をインスタンス化します.ビューが構造的であるので、全体のプロセスは本当に速いです.それでも、変数宣言は毎回呼び出されますFoo() は毎回呼び出されます.
IOSの14では解決策を使用することです@StateObject . 初期化子値は@autoclosure それで、それは毎回起動されません.そして、Swiftuiランタイムは、最初に作成された値がビューのすべてのレンダリングで使用可能であることを確認します.
さて、ここで問題です.IOSの14の養子縁組は本当に高速に成長しているにもかかわらず、それは非常には誰もがまだIOSの13互換性なしにアプリケーションを公開したいと考えている可能性が低いです.
そこで、ここで私はこの問題を回避するために来たという考えです.Swiftui 1.0では、我々はレンダリングの間の値を節約する唯一のツールは@State , オブジェクトをその属性で宣言された変数に格納しなければなりません.でも@State 単純な型のためだけのものであり、したがって、ObservableObject . つまり、実際にフレームワークによって観測される別の変数に値を複製する必要があるということです.幸運にも私たちが使用できる別の属性があります.@EnvironmentObject . この属性は、ObservableObject . ちょうど我々が必要!
解はそれから「ラッパー」ビューの状態の範囲内で値を保ち、ラップされたビューに手に入るためにオブジェクトを注入する.このパターンを繰り返し書く代わりに、この再利用可能なビューを提案します.
struct Observer<Obs, Content>: View where Obs: ObservableObject, Content: View {
    @State private var obs: Obs?
    private var content: Content
    private var initializer: () -> Obs

    init(_ initializer: @autoclosure @escaping () -> Obs, @ViewBuilder content: () -> Content) {
        self.content = content()
        self.initializer = initializer
    }

    var body: some View {
        if let obs = obs {
            content.environmentObject(obs)
        } else {
            Color.clear.onAppear(perform: initialize)
        }
    }

    private func initialize() {
        obs = initializer()
    }
}
全体の目的Color.clear ここでは、最初の外観を開始するにはObservableObject , しかし、一旦それがつくられるならばcontent オブジェクトを注入してレンダリングします.
これは、このような他のビューで簡単に使用できます.
Observer(MyModel()) { MyView() }
詳細を隠して、代わりに静的な値を宣言することを勧めますが、
// Just use `MyView` within other views
let MyView = Observer(MyModel()) { MyViewContent() }
このようにビューを宣言する必要があります.
struct MyViewContent: View {
  @EnvironmentObject var model: MyModel
}
余分な引数を渡す必要がある場合、代わりにクロージャとして宣言することができます.
// Use as `MyView(arg1, arg2)` this time
let MyView = { arg1, arg2 in Observer(MyModel(arg)) { MyViewContent(arg: arg2) }
私はあなたがこの便利な見つけると、IOSStateObject すべてのプロジェクトで.