SwiftUI:@ObservedObject 重绘每个视图 [英] SwiftUI: @ObservedObject redraws every view

查看:43
本文介绍了SwiftUI:@ObservedObject 重绘每个视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在 SwiftUI 中正确实现 MVVM 方式,所以我想出了这个(简化的)模型和视图模型:

I try to implement the MVVM way correctly in SwiftUI, so I came up with this (simplified) Model and ViewModel:

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

class ViewModel: ObservableObject {

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

}

View 中使用它工作正常,但我遇到了一些糟糕的性能问题,因为我使用一些计算属性和一些函数(以及 ViewModelcode>Model 本身比较复杂).但是让我们继续这个例子,因为它完美地展示了我认为 SwiftUI 本身的一个大问题.

Using this in a View works fine, but I experienced some bad performance issues, as I extended the ViewModel with some computed properties and some functions (as well the Model itself is more complicated). But let's stay with this example, because it demonstrates perfectly, what I think is a big problem in SwiftUI itself.

想象一下,你有这些视图来显示数据:

Imagine, you have those views to display the data:

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!打印输出为:

Now entering text into one of the TextField leads to a redraw of every View in my window! The print output is:

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.

As I can see, SwiftUI redraws every view, because every view is listening to the ObservedObject.

我如何告诉 SwiftUI,它只应该重绘那些视图,真正发生了任何变化的地方?

How can I tell SwiftUI, that it only should redraw those views, where really happened any changes?

推荐答案

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

If all your views observe the same thing, and that thing changes, then all your views will re-render. This is by definition. There's no option to configure to optionally update certain views.

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

That being said, you can work around it by manipulating what changes you'd like to publish.

例如;

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 会更改,但不会发布此更改,因此不会触发进一步的视图更新.你仍然可以从视图模型访问它.

Now when textfield changes, mytext changes, but this change won't be published thus won't trigger further view updates. And you still can access it from view model.

我个人建议使用 @State@EnvironmentObject 而不是视图模型"".它们是处理绑定和查看更新的内置方式,具有大量的安全保护和支持.

I personally would recommend using @State and @EnvironmentObject instead of "view model". They are the built-in way to handle binding and view updates with tons of safe-guards and supports.

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

Also you should use value type instead of reference type as much as possible. MVVM from other languages didn't take this into consideration.

这篇关于SwiftUI:@ObservedObject 重绘每个视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆