Revealed on: Might 22, 2024
One of many key options that was lacking from SwiftUI when it first shipped was a great way to do programmatic navigation. There have been some methods to deal with this earlier than iOS 16 launched NavigationPath
nevertheless it wasn’t very satisfying to make use of these APIs they usually may very well be relatively unreliable at occasions. To see an instance, check out this submit I wrote about dealing with deeplinks on iOS 14.
On this submit, I’d wish to revisit programmatic navigation by iOS 16’s NavigationPath
API which is a big leap ahead when it comes to developer expertise and reliability on the similar time.
On this submit we’ll take a look at:
- Understanding iOS 16’s navigation API
- Navigating by a NavigationPath
Understanding iOS 16’s navigation API
On iOS 16, Apple launched the NavigationStack
view. This can be a view that’s just about analogous to UIKit’s UINavigationController
and it permits builders to implement stack-based navigation. That is the sort of navigation that you simply’ll truly discover in most apps that mean you can navigate into objects which are proven in a listing for instance.
A navigation view in iOS has a stack of views that it holds on to as a hierarchy of how the person received to the place they at present are. For instance, the foundation view could be a listing, the following view could be a film view and the following one could be a view the place customers can view the forged of a film. Every view would exist on the stack and a person can navigate again one stage by swiping from the sting of their display.
I’m positive you’re acquainted with the UX of this.
The stack of views that represents the navigation hierarchy wasn’t obtainable to make use of till iOS 16. The principle distinction between UIKit’s UINavigationController
and the way NavigationStack
manages its navigation is that in SwiftUI we are able to truly navigate primarily based on fashions.
Because of this we are able to map cases of, for instance, a Film
mannequin to a MovieView
that may current a film to the person.
Primarily because of this we are able to mannequin a navigation hierarchy utilizing mannequin information relatively than views.
Let’s check out an instance of how we are able to arrange a NavigationStack
together with a element web page for a given mannequin kind. We gained’t introduce a NavigationPath
simply but. Behind the scenes our NavigationStack
will handle its personal path if we don’t present one so we’ll simply depend on that for now.
The code under defines a easy checklist view with NavigationLink
views to allow navigation. Discover that the NavigationLink receives a worth
as an alternative of a vacation spot
. Additionally, discover how we’re making use of a navigationDestination
view modifier to specify a vacation spot view for our mannequin.
struct ContentView: View {
@State personal var workout routines: [Exercise] = Train.pattern
var physique: some View {
NavigationStack {
ExercisesList(workout routines: workout routines)
.navigationDestination(for: Train.self, vacation spot: { train in
ExerciseDetail(train: train)
})
}
}
}
struct ExercisesList: View {
let workout routines: [Exercise]
var physique: some View {
Record(workout routines) { train in
NavigationLink(worth: train, label: {
ExerciseListItem(train: train)
})
}
.navigationTitle("My workout routines")
}
}
What’s particularly fascinating right here is the place we apply the navigationDestination
view modifier.
I selected so as to add it to my checklist. Because of this any NavigationLink
within my checklist with an occasion of Train
as its worth will use the vacation spot view that I supplied as its view. Because of this I can outline my vacation spot views multi function place which implies that I can shortly purpose about which view will likely be proven for a mannequin.
If I have been to outline a second navigationDestination
for a similar mannequin kind on my Record
, that second vacation spot would overwrite my first. This permits me to override the vacation spot if wanted so that every view can nonetheless explicitly outline its personal “exit views” nevertheless it’s not required. That is actually highly effective and permits for very versatile navigation setups.
At this level, we’re capable of push new fashions onto our navigation stack’s navigation path utilizing our navigation hyperlink and we’ve configured a vacation spot view utilizing the navigationDestination
view modifier.
Now let’s arrange a navigation path so we are able to begin performing some programmatic navigation, we could?
Navigating with a NavigationPath
A NavigationStack
could be arrange with a NavigationPath
object which can mean you can achieve management over the stack’s navigation hierarchy.
The only approach to arrange a NavigationPath
is as follows:
struct ContentView: View {
@State personal var workout routines: [Exercise] = Train.pattern
@State personal var path = NavigationPath()
var physique: some View {
NavigationStack(path: $path) {
ExercisesList(workout routines: workout routines)
.navigationDestination(for: Train.self, vacation spot: { train in
ExerciseDetail(train: train)
})
}
}
}
With this code, we’re not but doing something to realize management of our navigation path. We’re simply making an occasion of NavigationPath
and we go a binding to NavigationStack
. To any extent further, each time we navigate to a brand new view, the mannequin that’s used as a price will likely be added to the trail we created.
Primarily, when a person faucets on a NavigationLink
, we take the mannequin occasion that was handed as a worth
and it’s added to the navigation path robotically.
We are able to go any Hashable
mannequin as the worth for a navigation vacation spot and we are able to additionally combine fashions. So we may go cases of Train
, Int
, String
, and extra to the identical navigation path.
In reality, you usually don’t fear about which mannequin sorts you go. You simply go the mannequin that you could draw your vacation spot view and also you let the system deal with every thing else.
Let’s check out how we are able to change our NavigationLink
with a Button
so we are able to manually append our mannequin to the NavigationPath
that we’ve created earlier than.
We are able to create a binding to the NavigationPath
and we go it to the ExercisesList
, permitting it to append new objects to the trail which can enable the NavigationStack
to navigate to the vacation spot for our mannequin:
struct ContentView: View {
@State personal var workout routines: [Exercise] = Train.pattern
@State personal var path = NavigationPath()
var physique: some View {
NavigationStack(path: $path) {
// 1
ExercisesList(workout routines: workout routines, path: $path)
.navigationDestination(for: Train.self, vacation spot: { train in
ExerciseDetail(train: train)
})
}
}
}
struct ExercisesList: View {
let workout routines: [Exercise]
// 2
@Binding var path: NavigationPath
var physique: some View {
Record(workout routines) { train in
Button(motion: {
// 3
path.append(train)
}, label: {
ExerciseListItem(train: train)
})
}
.navigationTitle("My workout routines")
}
}
Earlier than I clarify the code, let me say that I don’t assume this can be a good concept. The code was higher with NavigationLink
. That stated, the purpose of this instance is to demo placing objects in a NavigationPath
programmatically which we are able to do from a button handler.
First, we go a binding to our navigation path to the checklist view. Because of this now our NavigationStack
and ExercisesList
each have entry to the very same NavigationPath
occasion.
The ExercisesList
was up to date to take a binding to a NavigationPath
, and we’ve swapped the NavigationLink
out in favor of a Button
. Within the button handler, I name append
with the Train
mannequin for the button on path
. This may add the mannequin to the trail which can trigger SwiftUI to navigate to the vacation spot view for that mannequin.
That is actually cool!
Along with appending parts to the trail, we are able to truly take away objects from the trail too by calling take away
on it.
We are able to even get the variety of objects on the trail to implement a “pop to root” fashion operate:
func popToRoot() {
path.removeLast(path.depend)
}
This operate will take away all parts from the navigation stack’s path, solely leaving its root
to be displayed.
The API for NavigationPath
is de facto versatile. You may even add a number of views in a single go, ensuing within the final added view changing into the highest every person others being a part of the stack so the person sees them once they navigate again.
In Abstract
With NavigationPath
we’ve gained a great deal of energy when it comes to having the ability to navigate programmatically. By leveraging model-based navigation we are able to symbolize a navigation stack’s hierarchy as information relatively than views, and we’re capable of go our NavigationPath
round by bindings in an effort to enable views to append new fashions to the trail.
Dealing with deeplinks and restoring navigation stacks with NavigationPath
is hundreds higher than it was pre iOS 16 and I’m positive that Apple will maintain enhancing NavigationPath
over time to make managing navigation by code higher and higher.