I have a SwiftUI view that is the landing page for my app with a body
like this:
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
VStack {
// my UI
}
.navigationDestination(for: NavigationDestination.self) { destination in
switch destination {
case .auth:
AuthView(navigationPath: navigationPath)
}
}
}
}
where NavigationDestination
is a private Hashable
enum.
After appending NavigationDestination.auth
to navigationPath
, I can correctly see my AuthView
when it doesn't include its own NavigationStack
:
var body: some View {
VStack {
// my UI
}
.navigationBarBackButtonHidden(true)
}
However, once I wrap it in its own NavigationStack
based on the path I passed into it:
var body: some View {
NavigationStack(path: $navigationStack) {
VStack {
// my UI
}
.navigationBarBackButtonHidden(true)
.navigationDestination(for: NavigationDestination.self) { destination in
switch destination {
case .signUp:
SignUpView(navigationPath: navigationPath)
}
}
}
}
(NavigationDestination
here is different from the initial view's, also a private Hashable
enum)
AuthView
no longer is presented and instead it is a view with a yellow triangle and exclamation point. Why does wrapping my second SwiftUI view in its own NavigationStack
prevent navigation to it?
When I remove the NavigationStack
from my second view, everything works fine, but I cannot navigate in the same manner I did on the first view. Currently, the only solution I see would be to have a root view that handles all navigation, but I do not like that idea for an app that needs to scale.
You should not nest NavigationStack
s within each other. Just put one NavigationStack
at the root, and if the views need access to the navigation path, pass it along your view hierarchy as a Binding
.
struct ContentView: View
{
@State var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
Root(path: $path)
}
}
}
You can still achieve decentralised navigation by having each view declare their own enum representing their destinations. Here is Root
and AuthView
, for example:
struct Root: View {
private enum NavigationDestinations {
case auth
}
@Binding var path: NavigationPath
var body: some View {
VStack {
Text("Root")
NavigationLink("Go to Auth", value: NavigationDestinations.auth)
}
.navigationDestination(for: NavigationDestinations.self) {
switch $0 {
case .auth:
AuthView(path: $path)
}
}
}
}
struct AuthView: View {
@Binding var path: NavigationPath
private enum NavigationDestinations {
case signUp
}
var body: some View {
VStack {
Text("Auth")
NavigationLink("Sign Up", value: NavigationDestinations.signUp)
}
.navigationDestination(for: NavigationDestinations.self) {
switch $0 {
case .signUp:
SignUpView(path: $path)
}
}
.navigationBarBackButtonHidden()
}
}
struct SignUpView: View {
@Binding var path: NavigationPath
var body: some View {
Text("Sign Up")
.navigationBarBackButtonHidden()
}
}