Sponsor Link
@State は struct にしか使えない
@State は、struct (というか、value-type) にしか使えないんです。
見てみます。
struct IntValueStruct { // (1)
var intValue:Int = 0
}
struct ContentView: View {
@State private var stateValue:IntValueStruct = IntValueStruct()
var body: some View {
VStack {
Spacer()
Text("stored value: \(stateValue.intValue)")
Stepper("value \(stateValue.intValue)", value: $stateValue.intValue)
Button(action: {print("\(stateValue.intValue)")}, label: {Text("check stateValue")})
Spacer()
}
.padding()
}
}
(1) で、@State の対象は、struct として定義しています。
class に変えてみます。
class IntValueClass { // (1) class に変えました
var intValue:Int = 0
}
struct ContentView: View {
@State private var stateClassValue:IntValueClass = IntValueClass()
var body: some View {
VStack {
Spacer()
Text("stored value: \(stateClassValue.intValue)")
Stepper("value \(stateClassValue.intValue)", value: $stateClassValue.intValue)
Button(action: {print("\(stateClassValue.intValue)")}, label: {Text("check Value")})
Spacer()
}
.padding()
}
}
“Check Value” ボタンでその時点での値を、Xcode のコンソールに出力することができます。出力してみると Stepper の + ボタンを押した数分増えた数値が、コンソールに表示されることが確認できます。ですので、値が変更されていないのではなく、ビューが更新されていないと確認できます。
つまり、@State は、class に対して使用すると、@Binding と組み合わせることで、値のリファレンスを渡すことはできていますが、その変更をフェッチし、ビューを更新することができていない ことがわかります。
まさしく、このケースが、@ObservedObject を使うケースとなります。
@ObservedObject
@ObservedObject の property wrapper を付与するためには、class が ObservableObject を conform していなければなりません。
@ObservedObject を使ったものを追記すると以下のようになります。
class IntValueClass :ObservableObject { // ObservableObject に 準拠
@Published var intValue:Int = 0 // @Published で変更を検知するプロパティを設定
}
struct ContentView: View {
@ObservedObject var obsClassValue:IntValueClass = IntValueClass()
var body: some View {
VStack {
Spacer()
Text("stored value: \(obsClassValue.intValue)")
Stepper("value \(obsClassValue.intValue)", value: $obsClassValue.intValue)
Spacer()
}
.padding()
}
}
実行してもらうとわかりますが、下側の UI は、期待通りに動作します。
StateObject 導入前は上記のコードでよかったのですが、Reference タイプのオブジェクトのライフサイクルをきちんと管理する StateObject の導入後は、上記コードの @ObservedObject を StateObject に読み替えてください。
この View 内で実体化したオブジェクトは、@StateObject を使用することで適切に管理してくれます。@ObservedObject は、外部で管理されていることを想定する property wrapper です。
ObservableObject とは? @Published とは?
ObservableObject とは、内部にobjectWillChange というプロパティを持つことを要求する Protocol です。objectWillChange のタイプは、Publisher です。
Apple のドキュメントは、こちら。
@Published は、そのプロパティの変更について、上記の Publisher を使って、通知します。
なお、@Published が付与されたプロパティの $ を渡すと、@Binding になります。
まとめ
設定対象 | |
@State | struct, enum |
@ObserervedObject | class |
- @ObservedObject として使用される class は、ObservableObject プロトコルに準拠するように定義されなければいけない
- 変更を監視するプロパティに、@Published を付与する
- @ObservedObject に含まれる @Published が付与されているプロパティは、@Binding で受ける
説明は以上です。
不明な点やおかしな点ありましたら、ご連絡いただけるとありがたいです。
Sponsor Link