Setting Up a Navigation Stack
Open the starter project for this chapter, and you’ll find a very early version of the flight-data app for an airport that you’ll use for this module. You’d likely get the flight information from an external API in a real-world app. For this lesson, you’ll use mock data created in the FlightData.swift file inside the Models folder.
The FlightData
class generates a schedule for fifteen days of flights with thirty flights per day starting with today’s date using the generateSchedule()
method. The class uses a seeded random number generator to produce consistent flight data every time, with only the start date changing. Now, open and examine FlightInformation.swift, which encapsulates information about flights you’ll display in this module.
Creating the Navigation List
Open WelcomeView.swift. The view includes a @StateObject
named flightInfo
that holds this mock data for the app.
Build and run the starter app. You’ll see a bare-bones implementation with a graphic and a single option to view the day’s flight status board.
In this app, you’ll see a list of flights. You can then select a flight and see more information about it. This pattern of choosing from a list of items to see more about that item is known as hierarchical navigation. The user can also return to an earlier view in the navigation stack. Lower views in the stack can also present multiple items and move further in the hierarchy. Hierarchical navigation works well when the user has little need to switch laterally between views where navigation flows from broader to more specific information at each level.
In SwiftUI, you implement hierarchical navigation using a NavigationStack
. A hierarchical layout has fewer top-level views than a flat layout, but each contains a more in-depth view stack beneath. The user may also have to backtrack through several layers of the navigation stack to find another view. Since you have a set of flights to display, you’ll build a List
to show them to the user.
Before the Spacer()
at the end of the view, add the following code:
// 1
List(flightInfo.flights) { flight in
// 2
Text(flight.statusBoardName)
}
// 3
.listStyle(.plain)
Here’s how this code displays the flights to the user:
- You display a list that iterates over a set of data, here an array of
FlightInformation
contained in theflights
property of theflightInfo
state. TheFlightInformation
struct implements theIdentifiable
protocol, which requires a uniqueid
property. SwiftUI uses this to distinguish between the different items for navigation. - You display the name and other end of the flight using the
statusBoardName
property. - Then, you specify the
plain
list style, which removes some default formatting and will show more text on the view.
Build and run the app. Now you’ll see a list of flights.
Now that you can show a list of flights, in the next section, you’ll add the ability to view more flight details.
Adding Navigation to a List
First, you’ll tell SwiftUI you want to use a List for navigation by wrapping it inside a NavigationStack
. In WelcomeView.swift, change the initial VStack
at the top of the view to:
NavigationStack {
This change will wrap the entire view inside the navigation structure. Only the part of the view inside the NavigationStack
becomes part of the navigation structure. In most cases, dedicating the whole view to navigation provides the best results.
It’s not required, and you could leave part of the view as a header if you only wrapped the List
inside the NavigationStack
.
Using a NavigationStack
tells SwiftUI you want to implement hierarchical navigation. Next, you need to tell SwiftUI what parts of the view trigger navigation and how to react when the user interacts with those elements.
The starter app already contains a simple view showing details for the flight. Inside the FlightDetails group, open FlightDetails.swift
. This view shows a few details about a flight passed to it.
Now back in WelcomeView.swift, change the list to:
List(flightInfo.flights) { flight in
// 1
NavigationLink(flight.statusBoardName, value: flight)
}
// 2
.navigationDestination(
// 3
for: FlightInformation.self,
// 4
destination: { flight in
FlightDetails(flight: flight)
}
)
These changes add support for navigation to the list’s rows. Here’s how they work:
- This
NavigationLink
view uses a convenience initializer that displays a string in aText
view. Thevalue
parameter tells SwiftUI to send theflight
property passed to the closure for theList
associated with this row to the next navigation component in the next step. - In step one, you told SwiftUI how to display a navigation link and defined the value SwiftUI associates with that row. The
navigationDestination(for:destination:)
modifier tells SwiftUI what to do with the value. This modifier connects values of a specified type with an action to perform. Notice that you apply it to theList
. The modifier will only apply toNavigationLink
s inside the List. Don’t place the modifier inside a looping container likeList
orScrollView
. - The
for
parameter tonavigationDestination(for:destination:)
specifies the type ofvalue
thenavigationDestination(for:destination:)
handles. PassingFlightInformation.self
tofor
tells SwiftUI to use this method for values of theFlightInformation
type. In step one, you passed an instance of aFlightInformation
struct through thevalue
parameter. SwiftUI connects the two and will use thisnavigationDestination(for:destination:)
for selectedNavigationLink
s. - The
destination
parameter tells SwiftUI what view to show when it’s passed the matching type. SwiftUI passed aFlightInformation
instance calledflight
that matches that passed to thevalue
parameter in step one. You display theFlightDetails
view passing along theflight
.
The preview will show you the new list. On iOS, you’ll get the small right-pointing disclosure arrow at the end of each row. This visual indicator shows the user that tapping the row will lead to more information, and SwiftUI automatically adds it to the List
inside a NavigationStack
:
Run the app and tap any flight. You’ll see the details for that flight.
Those are the basics of building a view navigation in SwiftUI:
- You wrap the navigable parts of the app inside a navigation view such as
NavigationStack
. - Then, you define the elements of the view that trigger navigation inside a
NavigationLink
view that defines what to display and the value associated with the display. - You then apply a
navigationDestination
modifier that tells SwiftUI what to do when the user selects aNavigationLink
. - You can mix
NavigationLink
s of different types by providingnavigationDestination
modifiers for each type, allowing a single element to perform various actions based on the kind of data.
Next, you’ll explore ways to customize the navigation and help users keep their place inside the navigation stack.