SwiftUI:@ObservedObject重绘每个视图

时间:2020-04-28 12:31:01

标签: swift swiftui

我尝试在SwiftUI中正确实现MVVM,因此我想到了这个(简化的)模型和ViewModel:

struct Model {
    var property1: String
    var property2: String
}

class ViewModel: ObservableObject {

    @Published var model = Model(property1: "this is", property2: "a test")

}

View中使用它可以很好地工作,但是我遇到了一些不良的性能问题,因为我在扩展ViewModel时使用了一些计算属性和某些函数(Model本身就是更复杂)。但是让我们继续看这个示例,因为它完美地展示了我认为SwiftUI本身存在的大问题。

想象一下,您有那些视图可以显示数据:

struct ParentView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {
        print("redrawing ParentView")
        return ChildView(viewModel: self.viewModel)
    }
}

struct ChildView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {
        print("redrawing ChildView")
        return VStack {
            ViewForTextField(property: self.$viewModel.model.property1)
            ViewForTextField(property: self.$viewModel.model.property2)
        }
    }

}

struct ViewForTextField: View {

    @Binding var property: String

    var body: some View {
        print("redrawing textView of \(self.property)")
        return TextField("...", text: self.$property)
            .textFieldStyle(RoundedBorderTextFieldStyle())
    }

}

现在将文本输入到TextField之一中会导致每个 View在我的窗口中重新绘制!打印输出为:

redrawing ParentView
redrawing ChildView
redrawing textView of this is
redrawing textView of a test
redrawing ParentView
redrawing ChildView
redrawing textView of this isa
redrawing textView of a test
redrawing ParentView
redrawing ChildView
redrawing textView of this isab
redrawing textView of a test
...

如我所见,SwiftUI重绘了每个视图,因为每个视图都在监听ObservedObject

我怎么能告诉SwiftUI,它应该只重绘那些视图,而实际上什么地方发生了更改?

2 个答案:

答案 0 :(得分:0)

实际上,MVVM表示每个视图都有自己的模型,该模型会随着模型更改而更新,而不是所有视图都一个模型。

因此viewModel中不需要遵守ParentView,因为它不依赖于它

struct ParentView: View {

    var viewModel: ViewModel // << just member to pass down in child

    var body: some View {
        print("redrawing ParentView")
        return ChildView(viewModel: self.viewModel)
    }
}

另一种方法是分解视图模型,以便每个视图都有自己的视图子模型,该子模型将管理自己视图的更新。

答案 1 :(得分:0)

如果所有视图都观察到相同的事物,并且该事物发生了变化,那么所有视图将重新呈现。这是根据定义。没有选项可以配置为选择性地更新某些视图。

话虽如此,您可以通过操纵要发布的更改来解决此问题。

例如;

class ViewModel: ObservableObject {

    @Published var model = Model(property1: "this is", property2: "a test")
    var mytext = "some text" // pass this as binding instead of model.propertyX
}

现在,当文本字段更改时,mytext也会更改,但是此更改不会发布,因此不会触发进一步的视图更新。您仍然可以从视图模型访问它。

我个人建议使用@State@EnvironmentObject而不是“视图模型”。它们是处理绑定并查看大量安全防护和支持的更新的内置方法。

此外,您应尽可能使用值类型而不是引用类型。其他语言的MVVM并未考虑这一点。