Open1

SwiftUI: delegateから呼んだ@Stateが更新されない

kabeyakabeya

Viewに、何かのdelegateプロトコルを実装し、そのメソッドから@Stateの変数を更新するんですが、これが全然表示に反映されない、という現象にハマリました。

以下のような奴です。

protocol SomeDelegate {
     func didSomething()
}

struct SomeView: View, SomeDelegate {
     var someManager: SomeManager
     @State var someText: String = ""
     init() {
         self.someManager = SomeManager()
         self.someManager.delegate = self
     }
     func didSomething() {
          self.someText = "done!"
     }
     var body: some View {
           Text(someText)
      }
}

何かのきっかけでSomeManager.delegate.didSomthing()を発火するんですが、実際にdidSomething()が呼ばれても、self.someTextは空文字列のままで表示は更新されません。

分かってしまえばなんてことはないのですが、これはdelegateを実装しているのがstructであることが原因です。

initで渡しているselfと、didSomething()が呼ばれたときのselfが違うんです。
関数呼び出しで引数としてstructの変数を渡して、渡した先の関数でそのstructのメンバを操作しているようなイメージですね。渡した先で操作した内容は、呼び出し元には反映されません。

修正するとすれば、以下のような感じでしょうか。

  1. ObservableObject@Observableclassを使ってdelegateを新たに作る
  2. そのクラスのdidSomething()でメンバ変数を更新する
  3. ObservableObjectの場合)更新するメンバ変数は@Publishedにする
  4. Viewにメンバとしてそのdelegateクラスのオブジェクトを持たせる(ObservableObjectの場合、@ObservedObjectにする)
  5. Viewはdelegateクラスのメンバ変数を見て表示を更新する