Reading time: 1 min
This quick recipe shows how to support nested NavigationLinks and allow the user to move back programatically. As always, SwiftUI throws a curveball even on trivial tasks such as this.
Here's what the end result looks like:
OK, so the situation you have is as following:
- You have a multi-level push navigation implemented with
NavigationLink
s that are triggered programatically - either with booleanisActive:
or optional-Identifiable
usingtag:selection:
. - You wish to manually navigate back from one of those screens to the previous one without using the built-in back button.
Here's some code that describes the setup:
struct NavPopTestingRoot: View {
@State private var isShown = false
var body: some View {
NavigationView {
NavigationLink("Next", isActive: $isShown) {
NavPopTesting(previousIsShown: $isShown)
}
}
}
}
struct NavPopTesting: View {
@Binding var previousIsShown: Bool
@State private var isShown = false
var body: some View {
VStack {
Button("Back") {
previousIsShown = false // dismiss programatically
}
NavigationLink("Next", isActive: $isShown) {
NavPopTesting(previousIsShown: $isShown)
}
}
.navigationBarBackButtonHidden(true)
}
}
Now, if you try running it, it works fine for the first dismiss - but for all subsequent ones, setting the isActive
binding to false or the selection
binding to nil
doesn't work - the screen isn't dismissed:
To work around this, add an .isDetailLink(false)
modifier to each NavigationLink
:
// ...
NavigationLink("Next", isActive: $isShown) {
NavPopTesting(previousIsShown: $isShown)
}
.isDetailLink(false) // HERE
// ...
After that, everything will work as expected:
SwiftUISegues framework has this already taken care of when you're using push
segues!