父视图中的State
变量发生更改时,SwiftUI将重绘子视图,并因此再次调用其构造函数。
考虑以下视图:
struct Parent: View {
@ObservedObject var viewModel: ParentViewModel
var body: some View {
VStack {
Child(viewModel: ChildViewModel())
}
}
}
Parent
视图中的任何状态更改(例如,其ViewModel中的Published
变量更改)都可能导致Child
视图被重绘,从而导致其viewModel
属性以接收ChildViewModel
的新实例。
重新绘制子视图时,我希望它仍能反映以前的状态,但是由于用ViewModel
的新实例再次调用了构造函数,所以不是这种情况。
让我通过强调我们使用 SwiftUI 1.0 作为开头,因此不能使用@StateObject
属性包装器。
我们尝试使用ViewModelProvider
类,该类对特定类型的ViewModels进行较长时间的缓存,以便某个ViewModel的连续初始化将返回相同的实例。如果一次只能有一个ViewModel实例(因此就是View),则此方法非常有效。但是,一旦添加了可以导航到同一View的新实例的View(添加了对多个具有相同类型的ViewModel的要求),便会分崩离析。
关于使上述模式起作用,我们还有其他一些想法,但是我无法撼动,应该有一种更好的方法来做到这一点,而不必跳过箍。
将Example
设置为rootView
的{{1}},再加上对按钮的某些疯狂点击,应该会触发打印语句,该语句会不时向您显示(甚至无法预测) )使用了SceneDelegate
的其他实例。
ChildViewModel
答案 0 :(得分:1)
使用StateObject
-仅创建一次:
struct ExampleView: View {
@State var selection = 0
var body: some View {
VStack {
Button("Tapped \(selection) times") {
self.selection += 1
}
ChildView() // don't pass the ViewModel
ChildView()
}
}
}
struct ChildView: View {
@StateObject var viewModel = ChildViewModel() // create here
var body: some View {
Text("Child")
}
}
另请参阅:
您可能需要改用@EnvironmentObject
。
在SceneDelegate
中:
let viewModel = ChildViewModel()
ExampleView()
.environmentObject(viewModel)
和ChildView
中的
struct ChildView: View {
@EnvironmentObject var viewModel: ChildViewModel // automatically injected
var body: some View {
Text("Child")
}
}
或者,您可以使用结构代替类:
struct ChildViewModel { ... }
struct ChildView: View {
@State var viewModel = ChildViewModel() // mark as `@State`
var body: some View {
Text("Child")
}
}