这是一个假设的SwiftUI主/细节视图对,它呈现了一个使用NavigationLink:value:label:
导航到子视图的按钮。子视图使用MVVM,并具有.navigationTitle
修饰符,该修饰符显示占位符,直到设置实际值(通过网络操作,为简洁起见省略)。
在第一次启动时,点击按钮确实导航到子视图,但是"Loading child…&;navigationTitle占位符永远不会改变"Alice"的实际值。尽管在视图模型的loadChild()
方法中设置。如果您返回并再次点击按钮,则所有后续导航都将正确设置navigationTitle。
但是,子视图具有if
条件。如果将if
条件替换为Text("whatever")
,并且重新构建并重新启动应用程序,则每次时,navigationTitle都会正确设置。为什么if
条件在视图中的存在会影响视图的navigationTitle的设置,而且只在第一次使用导航时才会影响?
import SwiftUI
// MARK: Data Structures
struct AppDestinationChild: Identifiable, Hashable {
var id: Int
}
struct Child: Identifiable, Hashable {
var id: Int
var name: String
}
// MARK: -
struct ChildView: View {
@ObservedObject var vm: ChildViewModel
init(id: Int) {
vm = ChildViewModel(id: id)
}
var body: some View {
VStack(alignment: .center) {
// Replacing this `if` condition with just some Text()
// view makes the navigationTitle *always* set properly,
// including during first use.
if vm.pets.count <= 0 {
Text("No pets")
} else {
Text("List of pets would go here")
}
}
.navigationTitle(vm.child?.name ?? "Loading child...")
.task {
vm.loadChild()
}
}
}
// MARK: -
extension ChildView {
@MainActor class ChildViewModel: ObservableObject {
@Published var id: Int
@Published var child: Child?
@Published var pets = [String]()
init(id: Int) {
self.id = id
}
func loadChild() {
// Some network operation would happen here to fetch child details by id
self.child = Child(id: id, name: "Alice")
}
}
}
// MARK: -
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink(value: AppDestinationChild(id: 42), label: {
Text("Go to child view")
})
.navigationDestination(for: AppDestinationChild.self) { destination in
ChildView(id: destination.id)
}
}
}
}
.task
的重点是摆脱对异步代码引用类型的需要,我建议您用state替换state对象,例如
@State var child: Child?
.task {
child = await Child.load()
}
你还可以捕获一个异常,并为错误消息提供另一种状态。