NavigationLink 隐藏目标视图,或导致无限视图更新
NavigationLink hides the Destination View, or causes infinite view updates
让我们考虑一下您有 ContentView
和 DestinationView
的情况。它们都依赖于一些共享数据,这些数据通常位于 @ObservedObject var viewModel
中,您可以通过 @EnvironmentObject
或直接在 init()
中从父级传递给子级。
本例中的 DestinationView
想要通过在 .onAppear
.
中获取一些额外的内容来丰富 viewModel
在这种情况下,当使用 NavigationLink
时,您可能会遇到 DestinationView
在获取内容时进入更新循环的情况,因为它还会更新父视图并且整个结构是重新绘制。
当使用 List
时,您显式设置了行的 ID,因此视图不会更改,但如果 NavigationLink
不在列表中,它将更新整个视图,重置其状态, 并隐藏 DestinationView
.
问题是:如何在需要的时候NavigationLink
update/redraw只?
在 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 出现在屏幕上之前 re-initializing 阻止它。
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 相关的问题。
让我们考虑一下您有 ContentView
和 DestinationView
的情况。它们都依赖于一些共享数据,这些数据通常位于 @ObservedObject var viewModel
中,您可以通过 @EnvironmentObject
或直接在 init()
中从父级传递给子级。
本例中的 DestinationView
想要通过在 .onAppear
.
在这种情况下,当使用 NavigationLink
时,您可能会遇到 DestinationView
在获取内容时进入更新循环的情况,因为它还会更新父视图并且整个结构是重新绘制。
当使用 List
时,您显式设置了行的 ID,因此视图不会更改,但如果 NavigationLink
不在列表中,它将更新整个视图,重置其状态, 并隐藏 DestinationView
.
问题是:如何在需要的时候NavigationLink
update/redraw只?
在 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 出现在屏幕上之前 re-initializing 阻止它。
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 相关的问题。