SwiftUI:@ObservedObject重绘每个视图

SwiftUI:@ObservedObject重绘每个视图,swift,swiftui,Swift,Swiftui,我试图在SwiftUI中正确实现MVVM方式,因此我提出了以下(简化)模型和视图模型: struct Model { var property1: String var property2: String } class ViewModel: ObservableObject { @Published var model = Model(property1: "this is", property2: "a test") } 在视图中使用它可以很好地工作,但是当我

我试图在SwiftUI中正确实现MVVM方式,因此我提出了以下(简化)模型和视图模型:

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

class ViewModel: ObservableObject {

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

}
视图中使用它可以很好地工作,但是当我使用一些计算属性和函数扩展
ViewModel
时,我遇到了一些糟糕的性能问题(以及
模型本身更复杂)。但让我们继续看这个例子,因为它完美地演示了我认为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())
    }

}
现在,将文本输入一个
文本字段
将导致我的窗口中每个
视图
的重画!打印输出为:

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,它只应该重新绘制那些视图,在哪里真正发生了任何更改?

实际上,MVVM意味着每个视图都有自己的模型,在模型更改时更新,而不是所有视图都有一个模型

因此,无需在
ParentView
中观察
viewModel
,因为它不依赖于它

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)
    }
}
结构父视图:视图{
var viewModel:viewModel/实际上,MVVM表示每个视图都有自己的模型,在模型更改时更新,而不是所有视图都有一个模型

因此,无需在
ParentView
中观察
viewModel
,因为它不依赖于它

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)
    }
}
结构父视图:视图{
var viewModel:viewModel/如果您的所有视图都观察到相同的内容,并且该内容发生了更改,则您的所有视图都将重新呈现。这是根据定义进行的。没有选项可以配置为可选地更新某些视图

也就是说,您可以通过操纵要发布的更改来解决这个问题

例如:

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

我个人建议使用and而不是“视图模型”。它们是处理绑定和视图更新的内置方式,具有大量的安全防护和支持


另外,您应该尽可能使用值类型而不是引用类型。其他语言的MVVM没有考虑到这一点。

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

也就是说,您可以通过操纵要发布的更改来解决这个问题

例如:

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

我个人建议使用and而不是“视图模型”。它们是处理绑定和视图更新的内置方式,具有大量的安全防护和支持


另外,您应该尽可能使用值类型而不是引用类型。其他语言的MVVM没有考虑到这一点。

谢谢您的回答。在我的例子中,我有不同的子视图,它们像选项卡式视图一样排列,它们都需要访问模型,所以我需要一个大模型,可以传递给视图。但我会的如果我可以拆分它们,试试看……顺便说一句,你的代码并没有改变主要问题:如果我将视图拆分为容器,则ChildView仍将被重新绘制,这不应该发生。谢谢你的回答。在我的例子中,我有不同的ChildView,它们都像选项卡视图一样排列,并且都需要访问模型,所以我需要一个大模型,我可以但是如果我可以拆分视图,我会尝试…顺便说一句,你的代码并没有改变主要问题:如果我将视图拆分到容器中,ChildView仍然会被重新绘制,这不应该发生