例えば、Binding を含むこういうのがあったとして。
struct Parent : View {
@State private var isOn = false
var body: some View {
VStack {
Rectangle ( ) . fill ( isOn ? . yellow : . gray)
Toggle ( isOn ? "ON" : "OFF" , isOn: $isOn)
}
}
}
親子分けした場合。
struct Parent : View {
@State private var isOn = false
var body: some View {
VStack {
Rectangle ( ) . fill ( isOn ? . yellow : . gray)
Child ( isOn: $isOn)
}
}
}
struct Child : View {
@Binding var isOn: Bool
var body: some View {
Toggle ( isOn ? "ON" : "OFF" , isOn: $isOn)
}
}
【SwiftUI】再描画の伝播 - @State と @Binding
@State の数が増えてくるとつらいので、
まとめて外出しにする方法はないのかな、と。
そして、子 View などへ上手に持ち運びたい。
@Observable と @Bindable
@Observable と @Bindable を使います。
Observable
Observable プロトコルの適合性を定義および実装します。
概要
このマクロは、カスタム タイプに監視サポートを追加し、タイプをObservableプロトコルに準拠させます。
Observable() | Apple Developer Documentation
Bindable
監視可能なオブジェクトの可変プロパティへのバインディングの作成をサポートするプロパティラッパータイプ。
概要
このプロパティ ラッパーを使用して、Observable プロトコルに準拠するデータ モデル オブジェクトの可変プロパティへのバインディングを作成します。
Bindable | Apple Developer Documentation
ドキュメントのコードを参考にしながら、
とりあえずは、クラスにまとめて外出しします。
@Observable
class StateModel : Identifiable {
var isOn = false
var label: String { isOn ? "ON" : "OFF" }
var color: Color { isOn ? . yellow : . gray }
}
struct Parent : View {
@Bindable private var model = StateModel ( )
var body: some View {
VStack {
Rectangle ( ) . fill ( model. color)
Toggle ( model. label, isOn: $model. isOn)
}
}
}
子に渡す
Binding を含む @State は、子の @Binding で受けてましたが。
@Observable クラスはどうやって渡すのか。
知ってる範囲であれこれ試してみました。
どうやら、これが標準的で良さげです。
@Observable
class StateModel : Identifiable {
var isOn = false
var label: String { isOn ? "ON" : "OFF" }
var color: Color { isOn ? . yellow : . gray }
}
struct Parent : View {
private let model = StateModel ( )
var body: some View {
VStack {
Rectangle ( ) . fill ( model. color)
Child ( model: model)
}
}
}
struct Child : View {
var model: StateModel
var body: some View {
@Bindable var model = model
Toggle ( model. label, isOn: $model. isOn)
}
}
試しながら思ったのは、
「クラスインスタンスのまま持ち歩いて、使う直前で @Bindable で Binding 化する」
のが良いですわ。
持ち回りがシンプルで楽だから。
データ自体は @Bindable なくても変化を検知して順方向には流れる。
ただ、いきなり出てきて意味不明で気持ちが悪かった。
@Bindable var model = model
公式ドキュメントでもあちこちにあって、気にはなっていました。
まとめ
最後に、ゴミを置いておきます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI
// 1
//struct Parent: View {
// @State private var isOn = false
//
// var body: some View {
// VStack {
// Rectangle().fill(isOn ? .yellow : .gray)
// Toggle(isOn ? "ON" : "OFF", isOn: $isOn)
// }
// }
//}
//struct Parent: View {
// @State private var isOn = false
//
// var body: some View {
// VStack {
// Rectangle().fill(isOn ? .yellow : .gray)
// Toggle(
// isOn ? "ON" : "OFF",
// isOn: Binding(
// get: { isOn },
// set: { value in isOn = value }
// )
// )
// }
// }
//}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// private let model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Toggle(
// model.label,
// isOn: Binding(
// get: { model.isOn },
// set: { value in model.isOn = value }
// )
// )
// }
// }
//}
// 3
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// @Bindable private var model = StateModel()
// //private var model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
//
// //@Bindable var model = model
// Toggle(model.label, isOn: $model.isOn)
// }
// }
//}
// 2
//struct Parent: View {
// @State private var isOn = false
//
// var body: some View {
// VStack {
// Rectangle().fill(isOn ? .yellow : .gray)
// Child(isOn: $isOn)
// }
// }
//}
//
//struct Child: View {
// @Binding var isOn: Bool
//
// var body: some View {
// Toggle(isOn ? "ON" : "OFF", isOn: $isOn)
// }
//}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// private let model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Child(model: model)
// }
// }
//}
//
//struct Child: View {
// var model: StateModel
//
// var body: some View {
// Toggle(
// model.label,
// isOn: Binding(
// get: { model.isOn },
// set: { value in model.isOn = value }
// )
// )
// }
//}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// @Bindable private var model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Child(model: model)
// }
// }
//}
//
//struct Child: View {
// @Bindable var model: StateModel
//
// var body: some View {
// Toggle(model.label, isOn: $model.isOn)
// }
//}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// private let model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Child(model: model)
// }
// }
//}
//
//struct Child: View {
// @Bindable var model: StateModel
//
// var body: some View {
// Toggle(model.label, isOn: $model.isOn)
// }
//}
// 4
@Observable
class StateModel: Identifiable {
var isOn = false
var label: String { isOn ? "ON" : "OFF" }
var color: Color { isOn ? .yellow : .gray }
}
struct Parent: View {
private let model = StateModel()
var body: some View {
VStack {
Rectangle().fill(model.color)
Child(model: model) // *
}
}
}
struct Child: View {
var model: StateModel
var body: some View {
@Bindable var model = model // *
Toggle(model.label, isOn: $model.isOn)
}
}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// private var model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Child(model: model)
// }
// }
//}
//
//struct Child: View {
// @Bindable var model: StateModel
//
// var body: some View {
// Toggle(model.label, isOn: $model.isOn)
// }
//}
//@Observable
//class StateModel: Identifiable {
// var isOn = false
// var label: String { isOn ? "ON" : "OFF" }
// var color: Color { isOn ? .yellow : .gray }
//}
//
//struct Parent: View {
// @State private var model = StateModel()
//
// var body: some View {
// VStack {
// Rectangle().fill(model.color)
// Child(model: $model)
// }
// }
//}
//
//struct Child: View {
// @Binding var model: StateModel
//
// var body: some View {
// Toggle(model.label, isOn: $model.isOn)
// }
//}
#Preview {
Parent()
.frame(width: 100, height: 100)
}
どのコメントブロックもきちんと動きます。
いや、動いてるように見えているだけかもしれません。