SwiftUI-IOS 16 NavigationStack-@StateObject init调用了两次



描述

我正在努力使我的应用程序适应IOS16中引入的新NavigationStack。当我的一个视图中有一个@StateObject变量时,我最终出现了一种奇怪的行为。

当我导航(使用新的.navigationDestination()修改器)到具有@StateObject的新视图时,该对象的init()块将运行两次。

视图的主体看起来是这样的:

VStack {
Text("Param: (intParam) and (viewModel.someData)")

Button("Do Something") {
viewModel.buttonTapped()
}
}

但是

如果我删除Button元素,@StateObjectinit()将只运行一次。

此外

如果我使用旧的NavigationLink(title:destination:)元素导航到新页面,它将运行@StateObjectinit()一次。

完整代码

struct ContentView: View {

var body: some View {
NavigationStack {
VStack {
NavigationLink(value: 16) {
Text("Tap Me (IOS 16)")
}
NavigationLink("Tap Me (Older)") {
Page2(intParam: 45)
}
}
.navigationDestination(for: Int.self) { i in
Page2(intParam: i)
}
.navigationTitle("Navigation")
}
}
}
struct Page2: View {
@StateObject var viewModel = ViewModel()
let intParam: Int

init(intParam: Int) {
self.intParam = intParam
print("Page2 view created")
}

var body: some View {
VStack {
Text("Param: (intParam) and (viewModel.someData)")

Button("Do Something") {
viewModel.buttonTapped()
}
}
}
}
extension Page2 {
@MainActor class ViewModel: ObservableObject {
@Published var someData = "something"

init() {
print("Page2 viewmodel created")
}

func buttonTapped() {
print("do something")
}
}
}

你知道这种行为的原因吗?

这不是一个bug,但它肯定会让开发人员感到困惑。当使用NavigationStack的destination方法导航到其他视图时,SwiftUI可能会多次调用navigationDestination中的闭包代码(大多数是两次)。但是,它使用上一次调用的结果来生成下一层视图。这意味着,无论创建多少StateObject实例,目标视图中都只会使用最后生成的实例。其他实例将自动销毁。

确切地说,这不是创建同一StateObject的多个实例,而是执行多次创建视图的操作,并且只保留上次创建的结果。

作为开发人员,我们只需要知道,当访问下一层视图时,StateObject实例是唯一的,当导航到更多层时,它仍然是唯一的。

这也从另一个角度告诉开发人员不要在init中执行任何昂贵的操作,而是将这些操作放在onAppear或task中。

至于回复中提到的onAppear可能会被多次调用(返回视图时)的问题,可以通过添加@State标志来解决。

最新更新