22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ObservableObject利用時のView更新タイミングを細かく制御する

Last updated at Posted at 2020-02-03

要点

  • Publishedは使わず、ObservableObjectプロトコルで宣言されているobjectWillChangeを使って実装する
  • Viewを更新したいタイミングでのみ、objectWillChangeのsendメソッドを呼ぶことで更新タイミングをコントロールできる

Publishedを使うパターン

SwiftUIでは、View内で@Stateプロパティを定義して状態管理を行う方法の他に、別クラスに状態変数を集めて管理する方法もあります。
これは ObservableObject@Published を組み合わせて使うやり方が一般的で、以下の実装手順で実現することが可能です。

実装手順

  1. ObservableObjectに準拠させたクラスを定義
  2. 状態の変更に応じてViewを更新させたいプロパティに @Published を与える
  3. View側では1.のインスタンスをプロパティに保持しておき、@ObservedObject のアノテーションを付与する
    @Publishedを付与したプロパティに更新が発生すると、その最新の値でViewが更新される
struct ContentView: View {
    // 状態変数をまとめて保持するインスタンス
    @ObservedObject var viewModel = ViewModel()
    
    var body: some View {
        VStack {
            // ViewModelのtitleプロパティに変更があると、自動でViewも更新される
            Text(viewModel.title)
            Button(action: {
                self.viewModel.onTapButton()
            }) {
                Text("Button")
            }
        }
    }
}

class ViewModel: ObservableObject {
    // ObservableObjectに準拠したクラス内の、@Publishedを付与したプロパティが監視対象となる
    @Published private(set) var title: String = "Hello, World!"
    
    func onTapButton() {
        self.title = "Hello, Published!"
    }
}
image

Publishedを使わないパターン

@Published を付与するだけで自動で更新されるのはとても便利な機能ですが、
Viewの更新タイミングを制御して、特定のタイミングでのみViewを再レンダリングさせたいといったケースではどうすれば良いでしょうか。

これは、ObservableObjectプロトコルで宣言されているobjectWillChangeを使って実装することで実現可能です。

実装手順

  1. ObservableObjectに準拠させたクラスを定義
  2. ObservableObjectPublisher 型の objectWillChange プロパティを定義
  3. 特定の値が更新される直前のタイミングで send メソッドを実行
    → データ更新後の最新の値でViewが更新される
import Combine

class ViewModel: ObservableObject {
    // ObservableObjectプロトコルへの適合
    private(set) var objectWillChange = ObservableObjectPublisher()
    
    private(set) var title: String = "Hello, World!" {
        willSet {
            // データの更新が発生する直前のタイミングでイベントを発生させる
            self.objectWillChange.send()
        }
    }
    
    func onTapButton() {
        self.title = "Hello, ObservableObjectPublisher!"
    }
}
image

上記のコードは @Published を付与して実装したパターンと同じ挙動になりますが、
send を明示的に呼び出す実装に変えたことにより、Viewの更新タイミングを条件で制御することができるようになりました。
これにより、Viewの更新タイミングを細かくコントロールすることが可能になります。

// example
private(set) var title: String = "Hello, World!" {
    willSet {
        if tapCount == 2 {
            self.objectWillChange.send()
        }
    }
}

余談

ちなみに、 send の制御を入れていたとしても、別で @State プロパティを定義していた場合、
その値に更新が入ると send の実行を待たずして、対象のプロパティが参照されてしまいます。

サンプルコード

22
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?