让我们考虑一下您拥有ContentView
和DestinationView
时的情况。两者都依赖于某些共享数据,这些共享数据通常位于@ObservedObject var viewModel
内部,您可以通过@EnvironmentObject
或直接在init()
内部将其从父级传递到子级。
在这种情况下,DestinationView
希望通过在.onAppear
内部获取一些其他内容来丰富viewModel。
在这种情况下,使用NavigationLink
时,您可能会遇到以下情况:DestinationView
在获取内容时进入更新循环,因为它还会更新父视图,并且整个结构都将重绘。
使用List
时,您显式设置行的ID,因此视图不会更改,但是如果NavigationLink
不在列表中,它将更新整个视图,重置其状态,并隐藏DestinationView
。
问题是:如何在需要时仅使NavigationLink
更新/重绘 ?
答案 0 :(得分:0)
在SwiftUI中,更新机制会比较View结构,以了解是否需要更新。我尝试了许多选项,例如制作ViewModel Hashable
,Equatable
和Identifiable
,迫使它仅在需要时更新,而没有用。
在这种情况下,仅 的工作解决方案是制作NavigationLink
包装器,并为其提供id
进行相等性检查,然后使用它。
struct NavigationLinkWrapper<DestinationView: View, LabelView: View>: View, Identifiable, Equatable {
static func == (lhs: NavigationLinkWrapper, rhs: NavigationLinkWrapper) -> Bool {
lhs.id == rhs.id
}
let id: Int
let label: LabelView
let destination: DestinationView // or LazyView<DestinationView>
var body: some View {
NavigationLink(destination: destination) {
label
}
}
}
然后在ContentView
中将其与.equatable()
一起使用
NavigationLinkWrapper(id: self.viewModel.hashValue,
label: myOrdersLabel,
destination: DestinationView(viewModel: self.viewModel)
).equatable()
有用的提示:
如果您的ContentView
也进行了一些可能会影响DestinationView的更新,则适合使用LazyView来防止Destination在屏幕上出现之前重新初始化。
struct LazyView<Content: View>: View {
let build: () -> Content
init(_ build: @autoclosure @escaping () -> Content) {
self.build = build
}
var body: Content {
build()
}
}
P.S:Apple似乎已在iOS14中修复了此问题,因此这只是与iOS13相关的问题。