Navigation Refined
Testing out some of the new toys
On the long list of interesting new things to check out from WWDC this year, the first one that I wanted to take a look at was the new NavigationStack
, as the limitations in the previous NavigationView
led to my previous project using UIKit to back navigation in a SwiftUI first project.
After a couple minutes playing around with the docs, I came up with:
A root view:
struct NewNavigationView: View {
private let numbers = [1, 2, 3]
private let strings = ["a", "b", "c"]
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
List {
Section("Numbers") {
ForEach(numbers, id: \.self) {
NavigationLink("\($0)", value: $0)
}
}
Section("Strings") {
ForEach(strings, id: \.self) {
NavigationLink($0, value: $0)
}
}
}
.navigationDestination(for: Int.self) { int in
NumberView(selection: int, navigationPath: $navigationPath)
}
.navigationDestination(for: String.self) { string in
StringView(selection: string, navigationPath: $navigationPath)
}
.navigationTitle("Root")
}
}
}
A view that displays details for a number:
struct NumberView: View{
private let numbers = [1, 2, 3, 4, 5]
let selection: Int
@Binding var navigationPath: NavigationPath
var body: some View {
List {
Section("Numbers") {
ForEach(numbers, id: \.self) {
NavigationLink("\($0)", value: $0)
}
}
Button("Pop to root") {
navigationPath = NavigationPath()
}
}
.navigationTitle("Detail \(selection)")
}
}
And a view that displays details for a string:
struct StringView: View {
private let strings = ["a", "b", "c", "d", "e"]
let selection: String
@Binding var navigationPath: NavigationPath
var body: some View {
List {
Section("Strings") {
ForEach(strings, id: \.self) {
NavigationLink("\($0)", value: $0)
}
}
Button("Pop to root") {
navigationPath = NavigationPath()
}
}
.navigationTitle("Detail \(selection)")
}
}
With just this code, I could:
- Keep pushing new views to the stack from the inner navigation links
- Pop to the root of the stack from within any view
It's particularly nice that you only need to add the navigationDestination
modifiers at the top level of the navigation hierarchy and any child views that have a navigation link will find that destination and use it to push onto the stack.
The only caveat that I ran into in my experimenting is that if you add a second navigationDestination
for the same type that another destination has claimed on a child node inside the NavigationStack
it's undefined behavior and so things break in interesting ways.