skip to Main Content

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")
}

2

Answers


  1. Chosen as BEST ANSWER

    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") {
                navigationPath.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.


  2. 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)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search