【SwiftUI】入れ子のObservableObjectで子の変更からViewを再描画させる
(宣伝)写真から3Dモデルを作成するiPhoneアプリを開発しました。よかったら使ってみてください。
apps.apple.com(ここから本題)
SwiftUI の ObservableObject は @Published
を指定したメンバ変数の更新を監視して、View を再描画させるために使います。
この記事では、入れ子になった ObservableObject を作り、子の更新を検知して親を使用している View を再描画させる方法を説明します。
元ネタは Stack Overflow。
SwiftUI は入れ子になった ObservableObject の監視をサポートしていません。
どういうことかというと、たとえば以下のように ObservableObject が ObservableObject を持っているとします。
class SubModel: ObservableObject { @Published var count = 0 } class ParentModel: ObservableObject { @Published var submodel: SubModel = SubModel() }
ParentModel のインスタンスを @ObservedObject
や @EnvironmentObject
で使用している View があるとき、 submodel.count
の変更があってもその View は再描画されません。
ただ、通常はこういうことはあまりやりません。
SubModel が ObservableObject ではなく通常の struct であれば、親は SubModel の変更を View に通知します。
これをするケースは、たとえば SubModel だけを使用する View があるときなどが想定できます。
さて、ParentModel を使用している View に submodel.count
の変更を受けて再描画させるためには、多少のトリックが必要になりまう。
それが以下のようなコードです。(Stack Overflow の回答と少しコードを変えてあります)
class SubModel: ObservableObject { @Published var count = 0 } class ParentModel: ObservableObject { var cancellable: AnyCancellable? = nil @Published var submodel: SubModel? { didSet { cancellable = submodel?.objectWillChange.sink { [weak self] (_) in self?.objectWillChange.send() } } } }
これは要するに子の ObservableObject に何か変更があったら objectWillChange
がイベントを送るのですが、それを親の ObservableObject の objectWillChange
にリレーしているということです。
これで、submodel.count の変更があったら ParentModel を使用している View が再描画されるようになります。