为什么添加中间视图或引用子属性会破坏我的数据绑定?

w1jd8yoj  于 2022-10-23  发布在  Swift
关注(0)|答案(1)|浏览(133)

我似乎误解了关于SWIFT/SWIFT用户界面的一些基本问题,因为我很难理解下面的最小示例在引入额外的视图或额外的间接级别时发生了什么。
下面是一个由ObservableObject层次结构组成的简单模型,以及一些操作它的简单视图。

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())
    }
}

现在,正如预期的那样,这可以正常工作:当移动滑块时,文本将更新为新值。
但是:

  • PickerView的直接使用替换为PickerViewWrapper打破了数据绑定:文本不再更新
  • PickerViewWrapper更改为引用将Model的示例作为输入(而不是使用子模型的示例)可以修复问题(但在我的实际场景中,这是不可能的,因为我确实希望引用顶级模型类的属性的属性)。

我在这里漏掉了什么简单的概念?

bvjxkvbb

bvjxkvbb1#

尝试这种方法,使用struct SubModel而不是嵌套ObservableObject。使用这种方法,SubModel的任何变化都将被Model直接观察到,即使使用中间视图也是如此。

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)
        }
    }
}

相关问题