Search code examples
swiftuiswiftui-navigationstack

View presented by navigationDestination:isPresented is always top of the navigation stack


When a view is pushed to the stack via navigationDestination:isPresented, it is always the top view in the stack, even if another view is pushed afterward. Is that the expected behaviour?

In the following code, when tapping the button on page 1, page 2 is pushed to the stack. When the navigationLink on page 2 is pushed, page 3 is pushed to the stack, but page 2 is moved 2 the top. So it is still page 2 that is visible. Navigate back shows page 3.

struct ContentView: View {
    
    @State var navigateToSecondPage = false
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("1st page")
                Button("Navigate to 2nd page") {
                    navigateToSecondPage = true
                }
            }
            .navigationDestination(isPresented: $navigateToSecondPage) {
                VStack {
                    Text("2nd page")
                    NavigationLink(value: SomeItem()) {
                        Text("Navigate to 3rd page")
                    }
                }
            }
            .navigationDestination(for: SomeItem.self) { item in
                Text("3rd page")
            }
        }
    }
}

struct SomeItem: Hashable {
    
}

So after navigating to page 3 the stack looks like: ['page 1', 'page 3', 'page 2'] If the navigationLink is pressed again the stack looks like: ['page 1', 'page 3', 'page 3', 'page 2']

Is that as expected when using navigationDestination:isPresented and navigationDestination:for?


Solution

  • I'm not sure if this is expected, but as the docs for navigationDestination(isPresented:destination:) say,

    In general, favor binding a path to a navigation stack for programmatic navigation.

    So I'm not surprised that there are cases that this doesn't work.

    Take the advice and bind a NavigationPath instead:

    @State var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Text("1st page")
                Button("Navigate to 2nd page") {
                    // NavigationDestinations is an enum
                    path.append(NavigationDestinations.page2)
                }
            }
            .navigationDestination(for: NavigationDestinations.self) { dest in
                if dest == .page2 {
                    VStack {
                        Text("2nd page")
                        NavigationLink(value: SomeItem()) {
                            Text("Navigate to 3rd page")
                        }
                    }
                }
            }
            .navigationDestination(for: SomeItem.self) { item in
                Text("3rd page")
            }
        }
    }