SwiftUI Tutorial: Navigation
In this tutorial, you’ll use SwiftUI to implement the navigation of a master-detail app. You’ll learn how to implement a navigation stack, a navigation bar button, a context menu and a modal sheet. By Audrey Tam.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
SwiftUI Tutorial: Navigation
45 mins
- Getting Started
- SwiftUI Basics in a Nutshell
- Declarative App Development
- Declaring Views
- Creating a Basic List
- The List id Parameter
- Starting Debug Preview
- Navigating to the Detail View
- Creating a Navigation Link
- Revisiting Honolulu Public Artworks
- Creating Unique id Values With UUID()
- Conforming to Identifiable
- Showing More Detail
- Handling Split View
- Declaring Data Dependencies
- Guiding Principles
- Tools for Data Flow
- Adding a Navigation Bar Button
- Reacting to Artwork
- Adding a Context Menu
- Creating a Tab View App
- Displaying a Modal Sheet
- UIViewRepresentable Protocol
- Adding a Button
- Showing a Modal Sheet
- Dismissing the Modal Sheet
- Bonus Section: Eager Evaluation
- Where to Go From Here?
Getting Started
Use the Download Materials button at the top or bottom of this tutorial to download the starter project. Open the PublicArt project in the PublicArt-Starter folder. You’ll build a master-detail app using the Artwork.swift and MapView.swift files already included in this project.
SwiftUI Basics in a Nutshell
SwiftUI lets you ignore Interface Builder and storyboards without having to write detailed step-by-step instructions for laying out your UI. You can preview a SwiftUI view side-by-side with its code — a change to one side will update the other side, so they’re always in sync. There aren’t any identifier strings to get wrong. And it’s code, but a lot less than you’d write for UIKit, so it’s easier to understand, edit and debug. What’s not to love?
The canvas preview means you don’t need a storyboard. The subviews keep themselves updated, so you don’t need a view controller either. And live preview means you rarely need to launch the simulator.
SwiftUI doesn’t replace UIKit — like Swift and Objective-C, you can use both in the same app. At the end of this tutorial, you’ll see how easy it is to use a UIKit view in a SwiftUI app.
Declarative App Development
SwiftUI enables you to do declarative app development: You declare both how you want the views in your UI to look and also what data they depend on. The SwiftUI framework takes care of creating views when they should appear and updating them whenever there’s a change to data they depend on. It recomputes the view and all its children, then renders what has changed.
A view’s state depends on its data, so you declare the possible states for your view, and how the view appears for each state — how the view reacts to data changes or how data affect the view. Yes, there’s a definite reactive feeling to SwiftUI! So if you’re already using one of the reactive programming frameworks, you’ll probably have an easier time picking up SwiftUI.
Declaring Views
A SwiftUI view is a piece of your UI: You combine small views to build larger views. There are lots of primitive views like Text
and Color
, which you can use as basic building blocks for your custom views.
Open ContentView.swift, and make sure its canvas is open (Option-Command-Return). Then click the + button or press Command-Shift-L to open the Library:
The first tab lists primitive views for layout and control, plus Other Views and Paints. Many of these — especially the control views — are familiar to you as UIKit elements, but some are unique to SwiftUI.
The second tab lists modifiers for layout, effects, text, events and other purposes like presentation, environment and accessibility. A modifier is a method that creates a new view from the existing view. You can chain modifiers like a pipeline to customize any view.
SwiftUI encourages you to create small reusable views, then customize them with modifiers for the specific context where you use them. And don’t worry, SwiftUI collapses the modified view into an efficient data structure, so you get all this convenience with no visible performance hit.
Creating a Basic List
Start by creating a basic list for the master view of your master-detail app. In a UIKit app, this would be a UITableViewController
.
Edit ContentView
to look like this:
struct ContentView: View {
let disciplines = ["statue", "mural", "plaque"]
var body: some View {
List(disciplines, id: \.self) { discipline in
Text(discipline)
}
}
}
You create a static array of strings, and you display them in a List
view, which iterates over the array, displaying whatever you specify for each item. And the result looks just like a UITableView
!
Make sure your canvas is open, then refresh the preview (click Resume or press Option-Command-P):
And there’s your list, just like you expected to see. How easy was that? No UITableViewDataSource
methods to implement, no UITableViewCell
to configure, and no UITableViewCell
identifier to misspell in tableView(_:cellForRowAt:)
!
The List id
Parameter
The parameters of List
are the array, which is obvious, and id
, which is less obvious. List
expects each item to have an identifier, so it knows how many unique items there are (instead of tableView(_:numberOfRowsInSection:)
). The argument \.self
tells List
that each item is identified by itself. This is allowed, as long as the item’s type conforms to the Hashable
protocol, which all the built-in types do.
Now take a closer look at how id
works: Add another "statue"
to disciplines
:
let disciplines = ["statue", "mural", "plaque", "statue"]
Refresh the preview: all four items appear. But, according to id: \.self
, there are only three unique items. A breakpoint might shed some light.
Add a breakpoint at Text(discipline)
.
Starting Debug Preview
The Live Preview button is the “play” button near the lower right corner of the canvas device. It runs the view in the canvas, but the ordinary live preview won’t stop at the breakpoint. Right-click or Control-click the Live Preview button, then select Debug Preview from the menu.
The first time you run Debug Preview, it will take a while to load everything. Eventually, execution stops at your breakpoint, and the Variables View displays discipline
:
Click the Continue program execution button: Now discipline = "mural"
.
Click Continue again to see discipline = "plaque"
.
Now, the next time you click the Continue button, what do you think will happen? It’s “statue” again! Surprised? Is this the fourth list item?
Well, click Continue twice more to see “mural” and “plaque” again. Then one final Continue displays the list of four items. So no, execution doesn’t stop for the fourth list item.
What you’ve just seen is: execution visited each of the three unique items twice; “statue” appeared only once on each run-through. So List
does see only three unique items. This isn’t a problem for this simple list of strings, but you’ll soon see an example of a non-unique id
problem.
You’ll also learn a better way to handle the id
parameter. But first, you’ll see how easy it is to navigate to a detail view.
Click the Live Preview button to stop it, and remove the breakpoint.