Remove screen from navigation stack in SwiftUI
Asked Answered
B

2

9

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?

Balmung answered 16/4, 2021 at 15:34 Comment(2)
please do not ask more than one question in one post, it makes harder to find the right answer for you also others to use your question to learn or study.Cw
To be fair, if one is coming at it from a UIKit perspective, this would be one concept. It just happens that in SwiftUI, it is more likely to be two separate issues.Selfforgetful
S
5

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://mcmap.net/q/134166/-how-can-i-pop-to-the-root-view-using-swiftui

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

Selfforgetful answered 16/4, 2021 at 16:15 Comment(0)
M
1

There is a new element in SwiftUI called NavigationStack which lets you manipulate the stack any way you want. I have been playing with it in an example project attempting to use it in a maintainable and programatic approach to coordinators in SwiftUI

Mach answered 10/8, 2022 at 14:25 Comment(2)
It's a pity that it's only supported from iOS 16 :)Balmung
Luckily there is a backport of the NavigationStack to iOS 14: github.com/lm/navigation-stack-backportSardius

© 2022 - 2025 — McMap. All rights reserved.