🔬
SwiftUIでView間受け渡しの @State @Binding @StateObject @ObservedObject 組合せ観測隊
SwiftUIでViewからViewにインスタンスを渡すときに @State
@Binding
@StateObject
@ObservedObject
を付けるが、これらをいろいろ組合せたときの挙動を調べた。
いくつか役に立つかどうかわからない裏技が得られた。興味がある人がいるかもしれないので載せておく。
やること
インスタンスを生成し、
- 生成View
- 受け取りView
の間で受け渡し、その挙動を観測する。受け渡すものを struct
と class
の両方で調べる。
受け渡すインスタンスが struct
or class
生成Viewでは @State
or @StateObject
or なし
受け取りViewでは @Binding
or @ObservedObject
計12パターンを調べていく。もちろん 2*3*2
である。
意味のない組み合わせもあると思うが、そんなことは気にしないでやる。
構造
- 受け渡すデータを
struct
とclass
で定義する - 渡すViewと受け取るViewを定義する
- 渡すViewでインスタンスを生成し、相手Viewに渡す
受け渡すデータのソース
struct StructA {
var value = 0
}
class ClassA: ObservableObject {
@Published var value = 0
}
渡すViewのソース
次のソースをいろいろ切り替えて実験を行う。
struct ContentView: View {
@State var structA = StructA()
@StateObject var classA = ClassA()
//@State
//@StateObject
var body: some View {
VStack(alignment: .leading) {
//このViewで取得して表示する部分
Text("structA in ContentView \(structA.value)")
Text("classA in ContentView \(classA.value)")
//相手Viewでに渡して相手Viewを表示する部分
//$は付けたり消したりする
SubStructView(a: $structA)
SubClassView(a: classA)
Button("add") {
structA.value += 1
classA.value += 1
}
.padding()
}
}
}
受け取り側Viewのソース
変数の宣言部で @Binding
と @ObservedObject
を切り替えて実験する。
struct SubStructView: View {
@Binding var a: StructA
//@Binding
//@ObservedObject
var body: some View {
Text("structA in SubStructView \(a.value)")
}
}
struct SubClassView: View {
@ObservedObject var a: ClassA
//@Binding
//@ObservedObject
var body: some View {
Text("classA in SubClassView \(a.value)")
}
}
結果
struct を受け渡し
生成 | 受け取り | 結果 | $ | 挙動/原因 |
---|---|---|---|---|
@State | @Binding | OK | いる | |
@State | @ObservedObject | エラー | structに@ObservedObjectがつけられない | |
@StateObject | @Binding | エラー | structに@StateObjectがつけられない | |
@StateObject | @ObservedObject | エラー | structに@StateObject / @ObservedObjectがつけられない | |
なし | @Binding | エラー | 渡すところでエラー | |
なし | @ObservedObject | エラー | structに@ObservedObjectがつけられない |
class を受け渡し
生成 | 受け取り | 結果 | $ | 挙動/原因 |
---|---|---|---|---|
@State | @Binding | エラーは出ず | いる | 値が反応しない |
@State | @ObservedObject | エラーは出ず | いらない | 生成Viewでは反応しない/受け取りVIewは反応する |
@StateObject | @Binding | エラー | 渡すところでエラー/何か裏技があるかも | |
@StateObject | @ObservedObject | OK | いらない | |
なし | @Binding | エラー | 渡すところでエラー/何か裏技があるかも | |
なし | @ObservedObject | エラーは出ず | いらない | 生成Viewでは反応しない/受け取りVIewは反応する |
まとめ
王道は
-
struct
には@State
@Binding
の組合せ -
class
には@StateObject
@ObservedObject
の組合せ
である。
裏技として
-
class
を、@State
@Binding
の組合せで受け渡しを行い、インスタンスが入れ替わったときに両方反応させ、中身が変わったときに反応させない -
class
を、@State
@ObservedObject
の組合せで受け渡しを行い、インスタンスが入れ替わったときに両方反応させ、中身が変わったときに@ObservedObject
の方だけ反応させる
が(使い所があれば)使える。
ちなみにインスタンス入れ替え部分のソースは以下である。
Button("change") {
let temp = ClassA()
temp.value = Int.random(in: 1...100)
classA = temp
}
続編
Discussion