These are the lesson notes for this day. Its build-along tutorial and flash cards have not been written yet.
NavigationSplitView shows two or three columns — sidebar, content, detail — on iPad and Mac, and collapses to a single stack on iPhone by itself. To adapt your own layouts, read the size class with @Environment(\.horizontalSizeClass), or use ViewThatFits, which tries its children in order and shows the first one that fits the space available.
Do not design for particular devices. As of iOS 27, iPhone apps can be resized — on iPad, and through iPhone Mirroring on a Mac — so an iPhone app may be shown at many different sizes.
ExerciseBuild a three-column app with NavigationSplitView and run it in both an iPhone and an iPad simulator. Then make a row that shows its icon and text side by side when there is room, and stacked when there is not, using ViewThatFits.
NavigationSplitView do on an iPhone?Presentations are driven by state. .sheet(isPresented:) shows a sheet while a Boolean is true. .sheet(item:) shows a sheet while an optional value is not nil, and passes that value to the sheet — use this form whenever the sheet displays or edits one particular thing. You dismiss a sheet by setting the state back, or from inside the sheet with @Environment(\.dismiss). .presentationDetents([.medium, .large]) makes a sheet resizable. .alert, .confirmationDialog, .popover and .fullScreenCover follow the same pattern, and as of iOS 27 alerts and confirmation dialogs accept an item binding too.
Coming from UIKit: there is no present(_:animated:) call. You change state, and the presentation follows.
struct TripRows: View {
let trips: [Trip]
@State private var editing: Trip? // nil means no sheet
var body: some View {
List(trips) { trip in
Button(trip.name) { editing = trip } // setting it presents the sheet
}
.sheet(item: $editing) { trip in // shown while `editing` is not nil
Text("Editing \(trip.name)")
.presentationDetents([.medium, .large])
}
}
}
Exercise
Make tapping a row open a sheet that edits that row's item, with medium and large detents and a Save button that dismisses it.
After this lesson you should be able to answer.sheet(item:) rather than .sheet(isPresented:)?