SwiftUI Navigation

Jun 20 2024 · Swift 5.9, iOS 17.0, Xcode 15.0

Lesson 01: Setting Up a Navigation Stack

Demo

Episode complete

Play next episode

Next
Transcript

Titles & Navigation Layout

When navigating to the details view, you may have noticed that the top of the view changed to a horizontal bar with a Back button on the left side. Tapping this button returns to the previous view.

[$//] flight-back.png “The back button on the details view”

This area of the view, called the Navigation Bar, provides a valuable resource to help the app user navigate hierarchical navigation. The most common additional element you might add to the navigation bar is a title for each view in your navigation stack. The title tells the user the current view’s purpose and gives the user an anchor in deeper stacks.

Go to WelcomeView.swift and add the following code just before the Spacer() view:

.navigationTitle("Mountain Airport")

Run the app, and you’ll see the new title on the initial view.

[$//] initial-w-title.png “Initial view with Title”

Note you apply the navigationTitle(_:) modifier inside the NavigationStack. You can only apply a single navigationTitle(_:) since SwiftUI will use the first and ignore the rest. This design lets you easily change the title of views deeper in the navigation hierarchy.

To do that, open FlightDetails.swift and add the following code to the end of the view:

.navigationTitle(flight.statusBoardName)

Build and run the app. Tap any flight, and you’ll see the statusBoardName in the navigation bar on a line below the Back button.

[$//] details-w-title.png “Title in Navigation Bar”

The default larger font size used for the title causes it to show in a new row. To provide more space for the view’s content, you can specify a smaller inline style for the view.

Add the following code after your navigationTitle(_:) modifier:

.navigationBarTitleDisplayMode(.inline)

Run the app and tap any flight. You’ll see the title now uses the default font size for the phone. The title on the details view appears on the same line as the Back button. This modifier only affects the size of the current view and doesn’t carry over to later views in the stack.

[$//] details-w-title-small.png “Title in Navigation Bar”

This title has an additional benefit beyond providing the user a consistent guide to their current place in the view hierarchy. Beginning in iOS 14, the user can also long-press the back button in the navigation bar to move anywhere up the view hierarchy in a single action. The text for each level will be the title provided by the view. If you don’t provide a title for a view, it’ll show as blank in the displayed list.

[$//] nav-path.png “Navigation Stack”

You may also notice that the title doesn’t show on the SwiftUI preview because the preview has no idea the view should appear as part of a navigation stack. To fix this, change the preview to:

NavigationStack {
  FlightDetails(
    flight: FlightData.generateTestFlight(date: Date())
  )
}

Now, the preview shows the navigation bar with the title. It doesn’t show the back button since the view is at the top of the stack in the preview.

In the next section, you’ll look at further navigation bar customization.

Adding Items to the Navigation Bar

As you’ve seen, creating a navigation view stack adds a navigation bar to each view. By default, the navigation bar only contains a button that returns to the previous view. In the last section, you added a title to this navigation bar. In this section, you’ll add a toggle to this app to hide flights that have landed or departed.

In WelcomeView.swift, add the following code after the declaration of flightInfo:

@State private var hidePast = false

You’ll set this state variable to hide past flights. Now, add a computed property after the new state variable to filter flights based on this variable:

var shownFlights: [FlightInformation] {
  hidePast ?
  flightInfo.flights.filter { $0.localTime >= Date() } :
  flightInfo.flights
}

Change the variable passed to List to use the computed property instead of flights:

List(shownFlights) { flight in

With those changes, you can filter the list of flights by changing the hidePast state variable using a toggle on the navigation bar. Add the following code after the navigationTitle(_:) modifier to add such a toggle:

.navigationBarItems(
  trailing: Button(
    hidePast ? "Show Past" : "Hide Past"
  ) {
      hidePast.toggle()
  }
)

The navigationBarItems(trailing:) modifier adds views to the trailing edge of the navigation bar. You’ll find a corresponding modifier, navigationBarItems(leading:), to add views to the leading edge, should you ever need that. These views will appear following the Back button.

You provide the views for the navigation bar inside the trailing closure. Here, you add a Button whose text depends on the current value of the hidePast state property. When tapped, the button toggles the hidePast property between true and false. Since hidePast is a state variable, SwiftUI will refresh and update the list when the value changes. Also, you use the Button style toggle to conserve space.

Build and run the app. You’ll see the newly added navigation button. Tap it to toggle between showing and hiding the past flights.

[$//] nav-toggle-button.png “Button Added to Navigation Bar”

You can add more items to the navigation bar, but avoid overcrowding it with too many controls. You’ll have trouble fitting more than one additional control on an iPhone-sized display.

See forum comments
Cinema mode Download course materials from Github
Previous: Setting Up a Navigation Stack Next: Conclusion