Navigation

Overview

iOS 16 introduced new ways to manage screen navigation.

See the WWDC 2022 video on the new navigation API titled The SwiftUI cookbook for navigation.

Also see the excellent YouTube videos from Stewart Lynch:

Basic Example

In this example the initial screen contains three navigation links and a Button. Tapping any of these navigates to a detail view for a selected kind of fruit.

SwiftUI Navigation Basic

struct ContentView: View {
@State private var showingBanana = false
private let fruits = ["Apple", "Banana", "Cherry"]

var body: some View {
NavigationStack {
VStack {
ForEach(fruits, id: \.self) { fruit in
NavigationLink(fruit, value: fruit)
}

Button("Go Ape!") { showingBanana.toggle() }
.buttonStyle(.borderedProminent)
}
.navigationTitle("Fruits")
.navigationDestination(for: String.self) { item in
switch item {
case "Apple":
// During development destinations can be simple
// `Text` views until real views are developed.
AppleView()
case "Banana":
BananaView()
case "Cherry":
CherryView()
default:
Text("unsupported fruit")
}
}
.navigationDestination(isPresented: $showingBanana) {
BananaView()
}
}
}
}

struct AppleView: View {
private let name = "Apple"

var body: some View {
Image(name)
.navigationTitle(name)
.navigationBarTitleDisplayMode(.automatic)
}
}

struct BananaView: View {
private let name = "Banana"

var body: some View {
Image(name).navigationTitle(name)
.navigationBarTitleDisplayMode(.inline)
}
}

struct CherryView: View {
private let name = "Cherry"

var body: some View {
Image(name).navigationTitle(name)
.navigationBarTitleDisplayMode(.large)
}
}

Example App

See NavigationStackDemo which demonstrates everything shared in the Steward Lynch videos linked above.

The app has four tabs.

Fruits tab

The first tab "Fruits" is implemented in the file FruitListView.swift. It uses a NavigationStack to allow the user to select a fruit from a List of NavigationLink views.

Fruits List

Nested inside the NavigationStack are instances of NavigationLink. This struct supports many initializers, some of which take a value argument. When these links are tapped, their value is placed on the stack.

When a fruit is selected, a view that displays "You chose {fruit-emoji}." is pushed onto the navigation stack.

Fruits Detail

Click "< Fruits" in the upper-left to return to the list of fruits. The "Show Favorite" button at the bottom demonstrates a NavigationList that is outside of the List. The "Smile" button at the bottom demonstrates a NavigationList that takes advantage of the fact that the only registered navigationDestination handles any String value.

Authors tab

The second tab "Authors" is implemented in the file AuthorListView. It uses a NavigationStack to allow the user to select an Author from a List of NavigationLink views.

Authors List

When an author is selected, a view that displays specific information about the author is pushed onto the navigation stack.

Author Detail

There are four navigationDestination registrations that handle the types:

The "Random" button selects a random author and displays information about that author.

Unlike in the Fruits tab, a path is passed to NavigationStack. This can be an instance of NavigationPath or any array of any type whose values conform to the Codable and Hashable protocols. Many built-in types such as String already conform to both of these protocols.

It seems the NavigationStack is only populated when the NavigationLink instances supply the value argument. This enables modifying the stack to navigate to another screen. For example, the following code navigates to the "root" screen:

path.removeLast(path.count)

If the path passed to NavigationStack is an Array of any type instead of a NavigationPath instance and it is held in state, navigate to the "root" screen by setting the that state variable to an empty array.

Countries Stack tab

The third tab "Countries Stack" uses a NavigationStack to allow the user to select a Country from a List of NavigationLink views.

Countries List

When a country is selected, a view that displays a list of cities in the country is pushed onto the navigation stack.

Cities List

Each city is represented by a NavigationLink. Tapping one of these pushes a view onto the stack that displays detailed information about that city. A star icon is displayed behind the information if the city is the capital of its country. A bar chart showing the population of each supported city in the same country is displayed below the city data. The "Back to Countries" button demonstrates popping back to the initial view in this tab.

City Detail

Countries Split tab

The fourth tab "Countries Split" is similar to the third tab, but uses a NavigationSplitView instead of a NavigationStack. A NavigationSplitView can have two of three sections arranged horizontally. These are named "sidebar", "content" (optional), and "detail". This app uses a three-column view where the first column contains a list of countries, the second column contains a list of cities in the selected country, and the third column contains detail about the selected city. Having a "Back to Countries" button doesn't apply in this scenario.

Countries List

Cities List

City Detail

Deep Linking

This app supports navigation via a URL.

To create a URL scheme:

To configure use of URLs:

To test use of URLS: