我尝试在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,它应该只重绘那些视图,而实际上什么地方发生了更改?
答案 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并未考虑这一点。