【问题标题】:SwiftUI: NavigationView/NavigationLink: launch with destination pushed?SwiftUI:NavigationView/NavigationLink:启动并推送目的地?
【发布时间】:2021-05-29 09:16:05
【问题描述】:

iOS Mail 应用启动时,您最近查看的邮箱已被推送到导航堆栈,即使它实际上是一个详细信息屏幕,并且根视图控制器是所有邮箱的列表。这在 UIKit 中是微不足道的,因为推送到导航控制器是微不足道的,并且可以在 viewDidLoad 或 applicationDidFinishLaunching 内部完成。

NavigationView 和 NavigationLink 是用于在 SwiftUI 中执行导航推送/弹出的 API,NavigationLink 支持几个模仿编程导航的 API - 但我不太清楚如何支持这个特定的用例。每次屏幕出现时都会调用onAppear 闭包(更像viewDidAppear 而不是viewDidLoad),因此设置与 NavigationLink 对应的 selection 值将导致在用户尝试弹出时重复推送该详细信息屏幕。我可以尝试对维护State 进行一些修改,以了解屏幕是否已被新查看,但这似乎并不理想。 (关于 NavigationLink 的 selection API 不稳定,我也遇到了一些问题,但这可能是一个单独的问题。)

有什么建议吗?如何在详细信息屏幕上启动应用程序,但在没有任何程序干预的情况下让 NavigationView 正常运行?

【问题讨论】:

    标签: ios swiftui swiftui-navigationlink


    【解决方案1】:

    如果您不需要以模式或动画的形式呈现您的视图,您应该使用编程导航(NavigationLink with selection binding)来获得所需的行为。

    例如,如果它是您的应用程序的根目录,则以下内容应该有效:

    struct Item: Identifiable {
        let id: Int
        let name: String
    }
    
    struct NormalNav: View {
        @State var items: [Item] = [
            .init(id: 0, name: "One"),
            .init(id: 1, name: "Two"),
            .init(id: 2, name: "Three")
        ]
    
        @State private var selection: Int? = 1
    
        var body: some View {
            NavigationView {
                List {
                    ForEach(items) { item in
                        NavigationLink(item.name,
                                       destination: Text(item.name),
                                       tag: item.id,
                                       selection: $selection)
                    }
                }
            }
        }
    }
    

    上面的视图应该直接打开到第二个项目(“Two”),没有动画。

    但是,有一个皱纹。如果您需要在 Sheet 中显示此视图,则会使用动画推送详细视图,这可能不是您想要的。

    this answer 的基础上,您可以在初始创建时抑制动画,但在后续导航时将其重新打开:

    struct ContentView: View {
        @State var items: [Item] = [
            .init(id: 0, name: "One"),
            .init(id: 1, name: "Two"),
            .init(id: 2, name: "Three")
        ]
    
        @State private var selection: Int? = nil
        @State var isAnimationDisabled = true
        @State var isListPresented = false
    
        var body: some View {
            Button("Show list") {
                isListPresented = true
            }.sheet(isPresented: $isListPresented) {
                NavigationView {
                    List {
                        ForEach(items) { item in
                            NavigationLink(item.name,
                                           destination: Text(item.name),
                                           tag: item.id,
                                           selection: $selection)
                        }
                    }.animations(disabled: isAnimationDisabled)
                }.onChange(of: selection) { value in
                    if value == nil {
                        isAnimationDisabled = false
                    }
                }.onAppear {
                    selection = 1
                }
            }
        }
    }
    
    extension View {
        func animations(disabled: Bool) -> some View {
            transaction { (tx: inout Transaction) in
                guard disabled else {
                    return
                }
                tx.disablesAnimations = true
                tx.animation = .none
            }
        }
    }
    

    【讨论】:

    • 谢谢 - 我在你给我的第一个例子中使用了同样的方法(使用标签和选择的 NavigationLink)并且遇到了问题,但我会再看看我自己的代码现在我有一个工作简化的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    • 2020-01-16
    • 2022-08-09
    • 2021-10-24
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多