Reputation: 4282
I appear to be misunderstanding something fundamental about Swift / SwiftUI, because I am struggling to understand what is happening in the minimal example below when I introduce an additional view or an additional level indirection.
Here's a simple model consisting of a hierarchy of ObservableObject
, and some simple views to manipulate it.
import SwiftUI
class Model : ObservableObject {
@Published var sub = SubModel()
}
class SubModel: ObservableObject {
@Published var hue: Double = 0.0
}
struct PickerView: View {
@Binding var hue: Double
var body: some View {
Slider(value: $hue, in: 0...255)
}
}
struct TextView: View {
@ObservedObject var model: Model
var body: some View {
Text(verbatim: String(model.sub.hue))
}
}
struct PickerWrapperView: View {
@ObservedObject var sub: SubModel
var body: some View {
PickerView(hue: $sub.hue)
}
}
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
VStack {
//PickerWrapperView(sub: model.sub)
PickerView(hue: $model.sub.hue)
TextView(model: model)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(model: Model())
}
}
Now, as is, this works as expected: when moving the slider, the text updates to the new value.
However:
PickerView
for PickerViewWrapper
breaks the data binding: the text no longer updatesPickerViewWrapper
to refer to take the instance of Model as input (as opposed to taking the instance of SubModel) fixes the problem (but this is a non starter in my real scenario where I do want to refer to a property of a property of my top model class).What probably simple concept am I missing here?
Upvotes: 0
Views: 68
Reputation: 36119
try this approach, using struct SubModel
instead of nesting ObservableObject. With this approach, any change to the SubModel
will be observed by the Model
directly, even with intermediate view.
class Model : ObservableObject {
@Published var sub = SubModel()
}
struct SubModel: Hashable { // <-- here
var hue: Double = 0.0
}
struct TextView: View {
@ObservedObject var model: Model
var body: some View {
Text("\(model.sub.hue)") // <-- here
}
}
struct PickerView: View {
@Binding var hue: Double
var body: some View {
Slider(value: $hue, in: 0...255)
}
}
struct PickerWrapperView: View {
@Binding var sub: SubModel
// @ObservedObject var model: Model
var body: some View {
PickerView(hue: $sub.hue)
// PickerView(hue: $model.sub.hue)
}
}
struct ContentView: View {
@StateObject var model = Model() // <-- for testing
var body: some View {
VStack {
PickerWrapperView(sub: $model.sub) // <-- here with binding
// PickerWrapperView(model: model) // <-- here with ObservedObject
// model.sub.hue, will be updated by the SLider
// PickerView(hue: $model.sub.hue)
// the Text in TextView will be updated whenever the model changes
TextView(model: model)
}
}
}
Upvotes: 1