父视图中的状态更改会创建子视图模型的新实例

时间:2020-09-18 08:35:18

标签: ios swift swiftui

问题

父视图中的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

1 个答案:

答案 0 :(得分:1)

SwiftUI 2

使用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")
    }
}

另请参阅:

SwiftUI 1

您可能需要改用@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")
    }
}