iOS 14から使えるStateObject、とても便利ですよね
ObservableObjectなどを初期化するときに便利です。
class Object: ObservableObject { @Published var value = "value" } struct Content: View { @StateObject var object = Object() var body: some View { Text(object.value) } }
ただ、これはiOS 14以降でしか使えないため、iOS 13に対応が必要な場合は使えません。代わりにObservedObjectを使います。
struct Content: View { @ObservedObject var object = Object() var body: some View { Text(object.value) } }
ただ、これはStateObjectと違い、Contentが再生成されるたびにObjectが再生成されてしまうため、期待通りの動作をしない場合があります。そのため、Objectを外部から与える必要があります。
struct Content: View { @ObservedObject var object: Object // どうやって初期化する? var body: some View { Text(object.value) } }
必要なオブジェクトが単一の場合はViewを生成する時に与えることで解決できますが、Listの各要素でそれぞれ必要な場合はどうすれば良いでしょうか。例えばNukeのFetchImageでは、画像1枚ごとにオブジェクトを初期化する必要があります。 そういった時に@StateObjectが便利なのですが、iOS 13では使えないため、@Stateと@ObservedObjectを別々に使うことで解決できます。
struct StateWrapper: View { @State var object = Object() var body: some View { Content(object: object) } } struct Content: View { @ObservedObject var object: Object var body: some View { Text(object.value) } }
StateWrapperでラップして、そこでは@Stateをストレージとして使いオブジェクトを保持しています。そのオブジェクトをbodyのObservedObjectを与えます。StateWrapperを挟むことで、それぞれのビューでオブジェクトを生成し、ビューの状態が変わった時も@Stateでオブジェクトが維持され、@StateObjectと同じ効果を得られます。
本来@Stateは値型に対して使うと、オブジェクトのメモリ管理と変更監視を行ってくれますが、参照型に使うと変更監視が使えませんが、メモリ管理はSwiftUIで行ってくれるようになります。そのため、Viewが再生成された時も同じインスタンスを参照するため、 @ObservedObjectと組み合わせることで @StateObject と同じ挙動を再現できます。
一旦これで解決できましたが、他に良い方法があれば教えてください!