【问题标题】:SwiftUI - Nested links within NavigationStack inside a NavigationSplitView not workingSwiftUI - NavigationSplitView 内的 NavigationStack 中的嵌套链接不起作用
【发布时间】:2022-08-10 14:38:36
【问题描述】:

我正在使用 ipadOS16/macOS13 中提供的新导航 API,但在确定如何在 macOS 13 上将 NavigationSplitView、NavigationStack 和 NavigationLink 组合在一起时遇到了一些麻烦(在 Macbook Pro M1 上测试)。相同的代码在 ipadOS 上也能正常工作。

我正在使用两列 NavigationSplitView。在 \'detail\' 部分中,我有一个包含在 NavigationStack 中的 SampleModel1 实例列表。在列表中,我已经为 SampleModel1SampleModel2 实例应用了 navigationDestination。

当我从列表中选择一个SampleModel1 实例时,我导航到一个详细视图,该视图本身包含一个SampleModel2 实例列表。我的意图是在单击 SampleModel2 实例之一时进一步导航到 NavigationStack 中,但不幸的是这似乎不起作用。 SampleModel2 实例是可选择的,但没有发生导航。

当我完全删除 NavigationSplitView 并仅使用 NavigationStack 时,问题确实存在不是出现,我可以成功导航到SampleModel2 实例。

这是我的示例代码:


// Sample model definitions used to trigger navigation with navigationDestination API.
struct SampleModel1: Hashable, Identifiable {
    let id = UUID()
    
    static let samples = [SampleModel1(), SampleModel1(), SampleModel1()]
}

struct SampleModel2: Hashable, Identifiable {
    let id = UUID()
    
    static let samples = [SampleModel2(), SampleModel2(), SampleModel2()]
}


// The initial view loaded by the app. This will initialize the NavigationSplitView
struct ContentView: View {
    
    enum NavItem {
        case first
    }
    
    var body: some View {
        NavigationSplitView {
            NavigationLink(value: NavItem.first) {
                Label(\"First\", systemImage: \"house\")
            }
        } detail: {
            SampleListView()
        }
    }
}

// A list of SampleModel1 instances wrapped in a NavigationStack with multiple navigationDestinations
struct SampleListView: View {
    
    @State var path = NavigationPath()
    @State var selection: SampleModel1.ID? = nil
    
    var body: some View {
        NavigationStack(path: $path) {
            List(SampleModel1.samples, selection: $selection) { model in
                NavigationLink(\"\\(model.id)\", value: model)
            }
            .navigationDestination(for: SampleModel1.self) { model in
                SampleDetailView(model: model)
            }
            .navigationDestination(for: SampleModel2.self) { model in
                Text(\"Model 2 ID \\(model.id)\")
            }
        }
    }
}

// A detailed view of a single SampleModel1 instance. This includes a list
// of SampleModel2 instances that we would like to be able to navigate to
struct SampleDetailView: View {
    
    var model: SampleModel1
    
    var body: some View {
        Text(\"Model 1 ID \\(model.id)\")
        
        List (SampleModel2.samples) { model2 in
            NavigationLink(\"\\(model2.id)\", value: model2)
        }
    }
}




struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

  • 我在 macOS 上的 beta 3 中遇到了类似的问题(根据 Asperi 的回答,我在 iPad 上可以正常工作)。我让它在 beta 2 中工作,但迁移到 b3 似乎已经破坏了它。

标签: swift swiftui swiftui-navigationsplitview macos-ventura


【解决方案1】:

我删除了这个不清楚的ZStack,一切正常。 Xcode 14b3 / iOS 16

//            ZStack {   // << this !!
                SampleListView()
//            }

【讨论】:

  • 感谢@asperi,似乎相同的代码确实可以在 ipadOS 上正常运行。我在 macOS Ventura 上特别遇到了这个问题。关于 ZStack,beta 3 发行说明提到以下内容:“NavigationSplitView 列中的条件视图无法更新某些状态更改。(91311311) 解决方法:将列的内容包装在 ZStack 中。”无论有没有,macOS 上的行为似乎都相同,因此我已将其从我的问题的示例代码中删除。
  • 我知道有条件的,但你在那里没有条件......它可能会影响某些东西......一切实际上都有效果,所以应该是有意义的。是的,我还没有安装 macOS 13,但是使用 ZStack 在 iPad 上也存在问题。
  • 我试图让它工作的应用程序确实有条件视图,但为了这个例子,我现在删除了 ZStack。不知何故,它在 macOS 上的行为有所不同..
【解决方案2】:

Apple 刚刚发布了解决该问题的 macos13 beta 5!他们还在我提出的反馈助理票上确认了这一点。

我在苹果开发者论坛上交叉发布了这个问题,并且用户 nkalvi 在苹果发布修复程序之前发布了一个解决这个问题的方法。我将在此处发布他的示例代码以供将来参考。


import SwiftUI

// Sample model definitions used to trigger navigation with navigationDestination API.
struct SampleModel1: Hashable, Identifiable {
    let id = UUID()
    
    static let samples = [SampleModel1(), SampleModel1(), SampleModel1()]
}

struct SampleModel2: Hashable, Identifiable {
    let id = UUID()
    
    static let samples = [SampleModel2(), SampleModel2(), SampleModel2()]
}


// The initial view loaded by the app. This will initialize the NavigationSplitView
struct ContentView: View {
    @State var path = NavigationPath()
    
    enum NavItem: Hashable, Equatable {
        case first
    }
    
    var body: some View {
        NavigationSplitView {
            List {
                NavigationLink(value: NavItem.first) {
                    Label("First", systemImage: "house")
                }
            }
        } detail: {
            SampleListView(path: $path)
        }

    }
}

// A list of SampleModel1 instances wrapped in a NavigationStack with multiple navigationDestinations
struct SampleListView: View {
    // Get the selection from DetailView and append to path
    // via .onChange
    @State var selection2: SampleModel2? = nil
    @Binding var path: NavigationPath
    
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Text("Path: \(path.count)")
                    .padding()
                List(SampleModel1.samples) { model in
                    NavigationLink("Model1: \(model.id)", value: model)
                }
                .navigationDestination(for: SampleModel2.self) { model in
                    Text("Model 2 ID \(model.id)")
                        .navigationTitle("navigationDestination(for: SampleModel2.self)")
                }
                .navigationDestination(for: SampleModel1.self) { model in
                    SampleDetailView(model: model, path: $path, selection2: $selection2)
                        .navigationTitle("navigationDestination(for: SampleModel1.self)")
                }
                .navigationTitle("First")
            }
            .onChange(of: selection2) { newValue in
                path.append(newValue!)
            }
        }
    }
}

// A detailed view of a single SampleModel1 instance. This includes a list
// of SampleModel2 instances that we would like to be able to navigate to
struct SampleDetailView: View {
    
    var model: SampleModel1
    @Binding var path: NavigationPath
    @Binding var selection2: SampleModel2?
    
    var body: some View {
        NavigationStack {
            Text("Path: \(path.count)")
                .padding()
            List(SampleModel2.samples, selection: $selection2) { model2 in
                NavigationLink("Model2: \(model2.id)", value: model2)
                // This also works (without .onChange):
                //                Button(model2.id.uuidString) {
                //                    path.append(model2)
                //                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 2021-05-27
    • 2014-11-13
    • 2022-09-29
    • 2012-04-15
    • 2016-03-28
    相关资源
    最近更新 更多