NavigationLink隐藏目标视图,或导致无限视图更新 [英] NavigationLink hides the Destination View, or causes infinite view updates

查看:60
本文介绍了NavigationLink隐藏目标视图,或导致无限视图更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们考虑使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆