NavigationSplitView (iOS 16) builds two- and three-column apps: sidebar, optional content list, and detail. Place it at the root of the scene; each column's root is wrapped in its own NavigationStack automatically.
Selection Drives the Columns
Pair value-based NavigationLinks with selection-based Lists. Pressing a link assigns its value to the list's selection binding, and the next column reads that selection.
NavigationSplitView {
List(selection: $selectedFolder) {
ForEach(Array(folders.keys.sorted()), id: \.self) { folder in
NavigationLink(value: folder) { Text(verbatim: folder) }
}
}
.navigationTitle("Sidebar")
} content: {
if let selectedFolder {
List(selection: $selectedItem) {
ForEach(folders[selectedFolder, default: []], id: \.self) { item in
NavigationLink(value: item) { Text(verbatim: item) }
}
}
} else {
Text("Choose a folder from the sidebar")
}
} detail: {
if let selectedItem {
Text(verbatim: selectedItem)
} else {
Text("Choose an item from the content")
}
}
A NavigationLink outside a selection-based list needs its own NavigationStack with a navigationDestination in that column, the pattern for push navigation inside the detail pane.
navigationDestination Placement Decides the Column
A navigationDestination outside any NavigationStack navigates in the next column of the split view. The same modifier inside a NavigationStack pushes onto that stack instead. Placement is the whole behavior switch.
Visibility, Style, and Width
Bind columnVisibility to change columns programmatically, for example a Focus button that hides everything but the detail.
NavigationSplitView(columnVisibility: $visibility) { /* ... */ }
Button("Focus") { visibility = .detailOnly }
NavigationSplitViewVisibility offers automatic, all, doubleColumn, and detailOnly. The style modifier picks how columns share space: balanced shrinks the detail to keep the first two columns visible, prominentDetail keeps the detail large.
.navigationSplitViewStyle(.balanced)
Fix a column's width with navigationSplitViewColumnWidth on that column's root view.
} content: {
ContentList()
.navigationSplitViewColumnWidth(300)
}