【问题标题】:Wrong List Item Selected in NavigationLink在 NavigationLink 中选择了错误的列表项
【发布时间】:2021-09-14 19:09:20
【问题描述】:

我正在尝试构建一个简单的导航,您可以在其中单击链接中的项目并从工作表视图弹回根控制器。正如您从下面的视频中看到的那样,当我点击列表中的某个项目时,加载了错误的项目(我单击的行与突出显示并加载的行之间存在偏移)。

我也收到错误SwiftUI encountered an issue when pushing aNavigationLink. Please file a bug.

这是我所有的代码:

import SwiftUI

struct ContentView: View {
    @State var rootIsActive:Bool = false
    
    var body: some View {
        NavigationView{
            AllProjectView(rootIsActive: self.rootIsActive)
        }
        .navigationBarTitle("Root")
        .navigationViewStyle(StackNavigationViewStyle())
        .environment(\.rootPresentationMode, self.$rootIsActive)
    }
}

struct AllProjectView: View {
    @State var rootIsActive:Bool = false
    @State var projects: [String] = ["1", "2", "3"]
    
    var body: some View{
        List{
            ForEach(projects.indices, id: \.self){ idx in
                ProjectItem(name: self.$projects[idx], rootIsActive: self.$rootIsActive)
            }
        }.navigationBarTitle("All Projects")
    }
}


struct ProjectItem: View{
    @Binding var name: String
    @Binding var rootIsActive: Bool
    
    init(name: Binding<String>, rootIsActive: Binding<Bool>){
        self._name = name
        self._rootIsActive = rootIsActive
    }
    
    var body: some View{
        NavigationLink(
            destination: ProjectView(name: self.name),
            isActive: self.$rootIsActive){
            Text(name)
        }
        .isDetailLink(false)
        .padding()
    }
}

struct ProjectView: View {
    var name: String
    @State var isShowingSheet: Bool = false
    @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
    @Environment(\.rootPresentationMode) private var rootPresentationMode: Binding<RootPresentationMode>
    
    var body: some View{
        VStack{
            Text(name)
            Button("Show Sheet"){
                self.isShowingSheet = true
            }
        }
        .sheet(isPresented: $isShowingSheet){
            Button("return to root"){
                self.isShowingSheet = false
                print("pop view")
                self.presentationMode.wrappedValue.dismiss()
                print("pop root")
                self.rootPresentationMode.wrappedValue.dismiss()
            }
        }
        .navigationBarTitle("Project View")
    }
}


// from https://stackoverflow.com/a/61926030/1720985

struct RootPresentationModeKey: EnvironmentKey {
    static let defaultValue: Binding<RootPresentationMode> = .constant(RootPresentationMode())
}

extension EnvironmentValues {
    var rootPresentationMode: Binding<RootPresentationMode> {
        get { return self[RootPresentationModeKey.self] }
        set { self[RootPresentationModeKey.self] = newValue }
    }
}

typealias RootPresentationMode = Bool

extension RootPresentationMode {
    
    public mutating func dismiss() {
        self.toggle()
    }
}

【问题讨论】:

  • ForEach 应该有一个明确的 id 或使 ProjectItem 可识别。实际上ForEach 不能区分项目,否则如果你不给他们一个单独的 id
  • 知道了 - 我在 ForEach 中添加了一个 id,但仍然遇到同样的问题。将更新帖子中的代码。

标签: swiftui swiftui-navigationlink


【解决方案1】:

您只使用了一个isRootActive 变量。而且,列表中的每个项目都会重复它。因此,只要点击列表中的 任何 项,每个 NavigationLinkisActive 属性就会变为true

除此之外,您的 isRootActive 现在实际上并没有做任何事情,因为您的“返回根”按钮已经这样做了:

self.isShowingSheet = false
self.presentationMode.wrappedValue.dismiss()

此时,没有什么可以忽略的了——它已经回到了根视图。

我删除了所有 root 和 isActive 的东西,你得到了这个:


struct ContentView: View {
    var body: some View {
        NavigationView{
            AllProjectView()
        }
        .navigationBarTitle("Root")
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct AllProjectView: View {
    @State var projects: [String] = ["1", "2", "3"]
    
    var body: some View{
        List{
            ForEach(projects.indices, id: \.self){ idx in
                ProjectItem(name: self.$projects[idx])
            }
        }.navigationBarTitle("All Projects")
    }
}


struct ProjectItem: View{
    @Binding var name: String
    
    var body: some View{
        NavigationLink(
            destination: ProjectView(name: self.name)
        ){
            Text(name)
        }
        .isDetailLink(false)
        .padding()
    }
}

struct ProjectView: View {
    var name: String
    @State var isShowingSheet: Bool = false
    @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
    
    var body: some View{
        VStack{
            Text(name)
            Button("Show Sheet"){
                self.isShowingSheet = true
            }
        }
        .sheet(isPresented: $isShowingSheet){
            Button("return to root"){
                self.isShowingSheet = false
                print("pop view")
                self.presentationMode.wrappedValue.dismiss()
            }
        }
        .navigationBarTitle("Project View")
    }
}

如果您在堆栈中有一个额外的视图,您需要一种方法来跟踪根是否处于活动状态。我在这里使用了一个自定义绑定,它将代表项目名称的可选String 转换为向下传递视图层次结构的Bool 值:

struct ContentView: View {
    var body: some View {
        NavigationView{
            AllProjectView()
        }
        .navigationBarTitle("Root")
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct AllProjectView: View {
    @State var projects: [String] = ["1", "2", "3"]
    @State var activeProject : String?
    
    func activeBindingForProject(name : String) -> Binding<Bool> {
        .init {
            name == activeProject
        } set: { newValue in
            activeProject = newValue ? name : nil
        }
    }
    
    var body: some View{
        List{
            ForEach(projects.indices, id: \.self){ idx in
                InterimProjectView(name: self.$projects[idx],
                                   isActive: activeBindingForProject(name: self.projects[idx]))
            }
        }.navigationBarTitle("All Projects")
    }
}

struct InterimProjectView: View {
    @Binding var name : String
    @Binding var isActive : Bool
    
    var body : some View {
        NavigationLink(destination: ProjectItem(name: $name, isActive: $isActive),
                       isActive: $isActive) {
            Text("Next : \(isActive ? "true" : "false")")
        }
    }
}

struct ProjectItem: View {
    @Binding var name: String
    @Binding var isActive: Bool
    
    var body: some View{
        NavigationLink(
            destination: ProjectView(name: self.name, isActive: $isActive)
        ){
            Text(name)
        }
        .isDetailLink(false)
        .padding()
    }
}

struct ProjectView: View {
    var name: String
    @Binding var isActive : Bool
    @State var isShowingSheet: Bool = false
    
    var body: some View{
        VStack{
            Text(name)
            Button("Show Sheet"){
                self.isShowingSheet = true
            }
        }
        .sheet(isPresented: $isShowingSheet){
            Button("return to root"){
                self.isShowingSheet = false
                print("pop root")
                self.isActive.toggle()
            }
        }
        .navigationBarTitle("Project View")
    }
}

【讨论】:

  • 这回答了你的问题吗?
猜你喜欢
  • 1970-01-01
  • 2016-09-12
  • 2015-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-17
相关资源
最近更新 更多