SwiftUI:@ObservedObject重绘每个视图

cgyqldqp  于 2023-06-04  发布在  Swift
关注(0)|答案(3)|浏览(224)

我尝试在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中输入文本会导致在我的窗口中重绘 everyView!打印输出为:

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重绘了 every view,因为每个view都在监听ObservedObject
我怎么能告诉SwiftUI,它只应该重新绘制那些视图,在那里真正发生了任何变化?

vc9ivgsu

vc9ivgsu1#

实际上,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)
    }
}

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

w1e3prcc

w1e3prcc2#

如果所有视图都观察到相同的事物,并且该事物发生了变化,则所有视图都将重新渲染。这是根据定义。没有配置选项来选择性地更新某些视图。
也就是说,您可以通过操纵想要发布的更改来解决它。
例如;

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
}

现在,当textfield更改时,mytext也会更改,但此更改不会被发布,因此不会触发进一步的视图更新。而且您仍然可以从视图模型访问它。
我个人建议使用@State和@EnvironmentObject而不是“视图模型”。它们是处理绑定和查看更新的内置方式,具有大量的安全防护和支持。
此外,你应该尽可能使用值类型而不是引用类型。其他语言的MVVM没有考虑到这一点。

envsm3lx

envsm3lx3#

这里有一点需要澄清:当SwiftUI框架加载你的视图时,这并不意味着它们必须被重绘。你的swiftui视图不是绘制代码,它们是SwiftUI框架的说明。
SwiftUI加载你的视图,比较viewids,并有选择地重绘已经更改的内容。有关详细说明,请参阅有关View Identities的WWDC视频。

相关问题