要点
- Publishedは使わず、ObservableObjectプロトコルで宣言されているobjectWillChangeを使って実装する
- Viewを更新したいタイミングでのみ、objectWillChangeのsendメソッドを呼ぶことで更新タイミングをコントロールできる
Publishedを使うパターン
SwiftUIでは、View内で@State
プロパティを定義して状態管理を行う方法の他に、別クラスに状態変数を集めて管理する方法もあります。
これは ObservableObject
と @Published
を組み合わせて使うやり方が一般的で、以下の実装手順で実現することが可能です。
実装手順
- ObservableObjectに準拠させたクラスを定義
- 状態の変更に応じてViewを更新させたいプロパティに
@Published
を与える - 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!"
}
}
Publishedを使わないパターン
@Published
を付与するだけで自動で更新されるのはとても便利な機能ですが、
Viewの更新タイミングを制御して、特定のタイミングでのみViewを再レンダリングさせたいといったケースではどうすれば良いでしょうか。
これは、ObservableObjectプロトコルで宣言されているobjectWillChangeを使って実装することで実現可能です。
実装手順
- ObservableObjectに準拠させたクラスを定義
-
ObservableObjectPublisher
型のobjectWillChange
プロパティを定義 - 特定の値が更新される直前のタイミングで
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!"
}
}
上記のコードは @Published
を付与して実装したパターンと同じ挙動になりますが、
send
を明示的に呼び出す実装に変えたことにより、Viewの更新タイミングを条件で制御することができるようになりました。
これにより、Viewの更新タイミングを細かくコントロールすることが可能になります。
// example
private(set) var title: String = "Hello, World!" {
willSet {
if tapCount == 2 {
self.objectWillChange.send()
}
}
}
余談
ちなみに、 send
の制御を入れていたとしても、別で @State
プロパティを定義していた場合、
その値に更新が入ると send
の実行を待たずして、対象のプロパティが参照されてしまいます。
サンプルコード