Search code examples
swiftuiswiftui-navigationlinkswiftui-navigationview

Remove screen from navigation stack in SwiftUI


I'm using NavigationLink to navigate screens in NavigationView. How can I remove screen from navigation stack? Not just hide the navigation "Back" button but completely remove screen from stack?

For example, I have screen chain like this: A -> B -> C How can I remove screen B to go back from C to A?

Or, another example, how to remove screen A and B, so the C screen will be the root?

Or all of this is impossible by conception of SwiftUI?


Solution

  • In terms of your first question (going from C to A), this is often called "popping to root" and has a number of solutions here on SO, including: https://stackoverflow.com/a/59662275/560942

    Your second question (replacing A with C as the root view) is a little different. You can do that by replacing A with C in the view hierarchy. In order to do this, you'd need to have some way to communicate with the parent view -- I chose a simple @State/@Binding to do this, but one could use an ObservableObject or even callback function instead.

    enum RootView {
        case A, C
    }
    
    struct ContentView : View {
        @State private var rootView : RootView = .A
        
        var body: some View {
            NavigationView {
                switch rootView {
                case .A:
                    ViewA(rootView: $rootView)
                case .C:
                    ViewC(rootView: $rootView)
                }
            }.navigationViewStyle(StackNavigationViewStyle())
        }
    }
    
    struct ViewA : View {
        @Binding var rootView : RootView
        
        var body: some View {
            VStack {
                Text("View A")
                NavigationLink(destination: ViewB(rootView: $rootView)) {
                    Text("Navigate to B")
                }
            }
        }
    }
    
    struct ViewB : View {
        @Binding var rootView : RootView
        
        var body: some View {
            VStack {
                Text("View B")
                NavigationLink(destination: ViewC(rootView: $rootView)) {
                    Text("Navigate to C")
                }
                Button(action: {
                    rootView = .C
                }) {
                    Text("Navigate to C as root view")
                }
            }
        }
    }
    
    struct ViewC : View {
        @Binding var rootView : RootView
        
        var body: some View {
            VStack {
                Text("View C")
                switch rootView {
                case .A:
                    Button(action: {
                        rootView = .C
                    }) {
                        Text("Switch this to the root view")
                    }
                case .C:
                    Text("I'm the root view")
                }
            }
        }
    }