如何在SwiftUI中使用`.navigationDestination`与`Button`来处理复杂的导航场景?

huangapple go评论77阅读模式
英文:

How to use .navigationDestination with Button in SwiftUI for complex navigation scenarios?

问题

如何在SwiftUI中使用.navigationDestination和Button处理复杂的导航情景?

我目前正在使用SwiftUI创建一个设置页面,其中包含大约10个按钮。由于需要自定义设计,并且每个按钮需要导航到不同的视图并传递不同的变量,因此这些按钮都不在ForEach或List中。

过去,我可能会使用NavigationLink(destination:label)来实现这一点,虽然这种方法仍然有效,但我遇到了一个问题。在其中一个或多个视图中,我需要使用NavigationLink(isActive),但这个方法现在已经不推荐使用了。

我发现了一个潜在更好的解决方案,使用.navigationDestination,但我在使其按预期工作方面遇到了问题。例如,我发现它在用于NavigationLink子视图时,当调用结束时并不会以编程方式关闭视图。

大多数SwiftUI教程和在线资源演示了使用List和ForEach以编程方式生成数据进行导航,而不是像在我的情况下那样简单的按钮点击。我已经观看了无数视频,甚至是苹果的文档,但对于这种新导航方式一无所知。

是否有人能够解释如何在此上下文中正确使用.navigationDestination以及它的要求?具体而言,我如何将其与Button(action:label)一起使用以导航到下一个视图,以及在NavigationLink子视图中它是如何行为的?

关于这个问题的任何帮助或指导将不胜感激。以下是我尝试实现的简化示例:

第一个视图:

NavigationStack() {
    NavigationLink(label: {
        Text("导航到第二个视图")
    }, destination: {
        SecondView()
    })
}

第二个视图:

Button(action: {
    isPresented = true //前往第三个视图
}) {
    Text("导航到第三个视图")
}
.navigationDestination(isPresented: $isPresented, destination: ThirdView())

第三个视图:

Button(action: {
    isPresented = false //返回第二个视图,但是这似乎不起作用
}) {
    Text("返回第二个视图")
}
英文:

How to use .navigationDestination with Button in SwiftUI for complex navigation scenarios?

I'm currently working on a settings page in SwiftUI which has approximately 10 buttons. Each of these buttons is not in a ForEach or List due to the need for a custom design, and each button needs to navigate to a different view with distinct variables being passed between them.

In the past, I would have used NavigationLink(destination:label) to achieve this, and while this method still works, I've run into an issue. In one or more of those views, I need to use NavigationLink(isActive), which is now deprecated.

I discovered a potentially better solution using .navigationDestination, but I'm having trouble making it work as expected. For instance, I found that it doesn't programmatically dismiss the view when it's finished being called if it's used in NavigationLink subviews.

Most of the SwiftUI tutorials and resources I've found online demonstrate navigation using List and ForEach with programmatically generated data, but not simple button clicks like in my scenario. And I have watched countless videos and even apple documentation and do not understand anything to do with this type of new navigation.

Could someone explain how to properly use .navigationDestination and its requirements in this context? Specifically, how can I use it with Button(action:label) to navigate to the next view, and how does it behave in NavigationLink subviews?

Any help or guidance on this matter would be greatly appreciated. Here's a simplified example of what I'm trying to accomplish:

First View:

NavigationStack() {
    NavigationLink(label: {
        Text("Navigate To Second View")
    }, destination: {
        SecondView()
    })
}

Second View:

Button(action: {
    isPresented = true //Goes to third view
}) {
    Text("Navigate to third view")
}
.navigationDestination(isPresented: $isPresented, destination: ThirdView())

ThirdView:

Button(action: {
    isPresented = false //Goes back to second view, however, this seems not to work
}) {
    Text("Navigate Back to second view")
}

答案1

得分: 0

你应该使用 NavigationPath

enum Route {
    case view2
    case view3
}

struct View1: View {
    @State private var navigationPath: [Route] = []
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("前往视图2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath)
                case .view3:
                    View3(navigationPath: $navigationPath)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("前往视图3") {
            navigationPath.append(.view3)
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("返回上一视图") {
            navigationPath.removeLast()
            
            // 或者 - 调用 `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}
英文:

You should use NavigationPath.

enum Route {
    case view2
    case view3
}

struct View1: View {
    @State private var navigationPath: [Route] = []
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("Go to view 2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath)
                case .view3:
                    View3(navigationPath: $navigationPath)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("Go to view 3") {
            navigationPath.append(.view3)
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("Pop view") {
            navigationPath.removeLast()
            
            // or - call `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

答案2

得分: 0

使用Eilons的答案,我成功地使它工作了。然而,它引发了一个错误,错误消息是只有根级别导航目标对于具有同质路径的导航堆栈才有效。经过一些调整,我为此头疼了几天,最终终于找到了答案。

首先,我删除了enum,因为它对我所做的是多余的。我首先添加了一个@State private var path = NavigationPath(),而不是@State private var navigationPath: [Route] = [] 数组。

以下是完整的代码:

struct View1: View {
    @State private var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            Button("前往视图2") {
                path.append("View2")
            }
            .background(Color.red)
            .navigationDestination(for: String.self) { route in
                switch route {
                case "View2":
                    View2(path: $path)
                case "View3":
                    View3(path: $path)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var path: NavigationPath
    
    var body: some View {
        Button("前往视图3") {
            path.append("View3")
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var path: NavigationPath
    
    var body: some View {
        Button("弹出视图") {
            path.removeLast()
            
            // 或者 - 调用 `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

希望这对某人有所帮助,因为我为此头疼了好几天。作为一个一直使用NavigationViewNavigationLink的人,切换到这个本应该很容易,但结果却很痛苦。

英文:

Using Eilons answer, I was able to successfully make it work. However, it caused an error of Only root-level navigation destinations are effective for a navigation stack with a homogeneous path. Making some adjustments, and having a headache over this for a few days, I was finally able to come up with an answer.

First, I got rid of the enum as it is redundant to what I did instead. I first added a @State private var path = NavigationPath() instead of the @State private var navigationPath: [Route] = [] array.

Here is the full code:

struct View1: View {
    @State private var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            Button("Go to view 2") {
                path.append("View2")
            }
            .background(Color.red)
            .navigationDestination(for: String.self) { route in
                switch route {
                case "View2":
                    View2(path: $path)
                case "View3":
                    View3(path: $path)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var path: NavigationPath
    
    var body: some View {
        Button("Go to view 3") {
            path.append("View3")
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var path: NavigationPath
    
    var body: some View {
        Button("Pop view") {
            path.removeLast()
            
            // or - call `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

I hope this helps someone as I had a headache over this for days. As someone who always used NavigationView & NavigationLink, switching to this was supposed to be easy, but turned out to be a pain.

huangapple
  • 本文由 发表于 2023年6月2日 09:42:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定