NavigationLink隐藏目标视图,或导致无限视图更新 [英] NavigationLink hides the Destination View, or causes infinite view updates
问题描述
让我们考虑使用 ContentView
和 DestinationView
时的情况.它们都依赖于某些共享数据,这些共享数据通常位于 @ObservedObject var viewModel
内部,您可以通过 @EnvironmentObject
从父级传递给子级,也可以直接在内部init()
.在这种情况下, DestinationView
希望通过在 .onAppear
中获取一些其他内容来丰富viewModel.
Let us consider the situation when you have ContentView
and DestinationView
. Both of them depend on some shared data, that typically lies inside the @ObservedObject var viewModel
, that you pass from parent to child either via @EnvironmentObject
or directly inside init()
.
The DestinationView
in this case wants to enrich the viewModel by fetching some additional content inside .onAppear
.
在这种情况下,当使用 NavigationLink
时,在获取内容时 DestinationView
会进入更新循环的情况,因为它还会更新父视图和整个结构都被重画了.
In this case, when using NavigationLink
you might encounter the situation when the DestinationView
gets into an update loop when you fetching content, as it also updates the parent view and the whole structure is redrawn.
使用 List
时,您显式设置行的ID,因此视图不会更改,但是如果 NavigationLink
不在列表中,它将更新整个视图,重置其状态并隐藏 DestinationView
.
When using the List
you explicitly set the row's ids and thus view is not changed, but if the NavigationLink
is not in the list, it would update the whole view, resetting its state, and hiding the DestinationView
.
问题是:如何使 NavigationLink
仅在需要时更新/重绘 ?
The question is: how to make NavigationLink
update/redraw only when needed?
推荐答案
在SwiftUI中,更新机制会比较View结构,以了解是否需要更新.我尝试了许多选项,例如使ViewModel Hashable
, Equatable
和 Identifiable
,迫使它仅在需要时进行更新,但均无法正常工作.
In SwiftUI the update mechanism compares View structs to find out whether they need to be updated, or not. I've tried many options, like making ViewModel Hashable
, Equatable
, and Identifiable
, forcing it to only update when needed, but neither worked.
仅 可行的解决方案是制作一个 NavigationLink
包装器,并为其提供 id
进行相等性检查,并改用它
The only working solution, in this case, is making a NavigationLink
wrapper, providing it with id
for equality checks and using it instead.
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在屏幕上出现之前重新初始化.
If your ContentView
also does some updates that would impact the DestinationView it's suitable to use LazyView to prevent Destination from re-initializing before it's even on the screen.
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相关的问题.
P.S: Apple seems to have fixed this issue in iOS14, so this is only iOS13 related issue.
这篇关于NavigationLink隐藏目标视图,或导致无限视图更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!