NavigationSplitView 是 SwiftUI 中的一种导航视图结构,适用于多栏导航界面,特别是在 iPad 或 macOS 等大屏设备上。它将界面分为主列表(主视图)和详细内容(详细视图)两个或多个部分。
基本语法
NavigationSplitView 使用一个三栏结构:
1、Sidebar(侧边栏) – 左侧栏,用于展示主导航列表。
2、Content(内容) – 中间栏,用于展示相关内容。
3、Detail(详细视图) – 右侧栏,用于显示选中项的详细信息。
NavigationSplitView {
SidebarView()
} content: {
ContentView()
} detail: {
DetailView()
}
参数说明
columnVisibility
类型:Binding<NavigationSplitViewVisibility>
用于指定分栏视图中各栏的显示状态。NavigationSplitViewVisibility 是一个枚举类型,包含以下选项:
.all:显示所有分栏(默认行为)。
.detailOnly:仅显示详细视图(右侧的分栏)。
.automatic:由系统根据设备类型和屏幕大小自动选择显示哪些分栏。
示例
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationSplitView(columnVisibility: .constant(.all)) {
// Sidebar
List(0..<10, id: \.self) { item in
Text("Item \(item)")
}
.navigationTitle("Sidebar")
} content: {
// Main Content
Text("Main Content View")
} detail: {
// Detail View
Text("Detail View")
}
}
}
#Preview {
ContentView()
}
效果
1、columnVisibility: .constant(.all) 保证:
左侧的 Sidebar、中间的 Content View、右侧的 Detail View 同时显示。
适用于 iPad 或 Mac 等大屏设备。
2、如果修改为 .constant(.detailOnly),将只显示右侧的 Detail View,隐藏其余部分。
动态控制显示状态
如果需要根据用户操作或其他条件动态调整分栏显示状态,可以用绑定一个 State 或 Environment 变量来替代 .constant。
@State private var visibility: NavigationSplitViewVisibility = .all
NavigationSplitView(columnVisibility: $visibility) {
// Sidebar
List(0..<10, id: \.self) { item in
Text("Item \(item)")
}
.navigationTitle("Sidebar")
} detail: {
// Detail View
Text("Detail View")
}
这种写法允许用户通过按钮或其他方式动态调整分栏的显示状态。
preferredCompactColumn
这是一个绑定(Binding),用于指定默认优先显示的列。
类型:Binding<NavigationSplitViewColumn?>
可取值:
.sidebar: 优先显示侧边栏(Sidebar)。
.content: 优先显示中间内容(Content,如果有)。
.detail: 优先显示详细视图(Detail)。
nil: 不优先显示任何列。
NavigationSplitView(preferredCompactColumn:) 是用于配置当 NavigationSplitView 在紧凑模式(如 iPhone 的竖屏模式)下运行时,默认显示的列。这个参数的作用是指定在紧凑模式下哪个列会被优先展示。
1、优先显示 Detail 列
当应用在紧凑模式(如 iPhone 竖屏)下运行时,默认展示 Detail 列。
import SwiftUI
struct ContentView: View {
@State private var compactColumn: NavigationSplitViewColumn = .detail
var body: some View {
NavigationSplitView(preferredCompactColumn: $compactColumn) {
List(0..<10, id: \.self) { item in
Text("Sidebar Item \(item)")
}
.navigationTitle("Sidebar")
} detail: {
Text("Detail View")
}
}
}
2、优先显示 Sidebar 列
@State private var compactColumn: NavigationSplitViewColumn? = .sidebar
适用场景
1、在 iPhone 上运行 NavigationSplitView:
在紧凑模式下,无法同时显示多列,必须指定一个优先展示的列。
通过 preferredCompactColumn,可以选择默认显示的列(如 Sidebar 或 Detail)。
2、动态切换列:
可以通过绑定动态切换当前紧凑模式下显示的列。例如,用户操作时切换显示内容。
注意事项
绑定动态更新:preferredCompactColumn 是一个 Binding,可以动态更新其值来切换显示的列。
无效时的行为:如果设置的列在当前布局下无效(例如,没有定义 Sidebar,但设置为 .sidebar),系统将自动选择一个有效的列。
完整示例
以下是一个 iPad 风格的导航分栏示例,显示水果列表,点击后显示详细信息:
import SwiftUI
struct ContentView: View {
@State private var selectedFruit: String? // 用于记录当前选中的水果
var body: some View {
NavigationSplitView {
// Sidebar
List(["Apple", "Banana", "Cherry", "Date"], id: \.self, selection: $selectedFruit) { fruit in
Text(fruit)
}
.navigationTitle("Fruits")
} content: {
// Content View
if let selectedFruit = selectedFruit {
Text("You selected: \(selectedFruit)")
.font(.title)
} else {
Text("Select a fruit")
.foregroundColor(.gray)
}
} detail: {
// Detail View
if let selectedFruit = selectedFruit {
Text("Details for \(selectedFruit)")
.font(.largeTitle)
} else {
Text("No fruit selected")
.foregroundColor(.gray)
}
}
}
}
解释
1、NavigationSplitView
定义了一个分栏导航界面,包含三个部分:
Sidebar: 用于主导航(如水果列表)。
Content: 显示选中项的相关信息。
Detail: 显示更详细的信息。
2、状态绑定:$selectedFruit
使用 @State 管理选中项。
List 中的 selection 属性会自动绑定到状态,当用户选择列表项时,状态会更新。
3、分栏适配:
在 iPad 和 macOS 上,会显示三栏布局。
在 iPhone 上,分栏会以堆栈式呈现,每次只显示一栏。
动态生成数据
也可以通过动态数据源生成列表:
struct ContentView: View {
@State private var selectedItem: Item? // 选中的项
let items = [
Item(name: "Swift", description: "A powerful programming language."),
Item(name: "SwiftUI", description: "Declarative UI framework."),
Item(name: "Combine", description: "Reactive programming framework.")
]
var body: some View {
NavigationSplitView {
// Sidebar
List(items, id: \.self, selection: $selectedItem) { item in
Text(item.name)
}
.navigationTitle("Topics")
} content: {
// Content View
if let selectedItem = selectedItem {
Text(selectedItem.name)
.font(.title)
} else {
Text("Select a topic")
.foregroundColor(.gray)
}
} detail: {
// Detail View
if let selectedItem = selectedItem {
Text(selectedItem.description)
.font(.body)
} else {
Text("No topic selected")
.foregroundColor(.gray)
}
}
}
}
struct Item: Identifiable,Hashable {
let id = UUID()
let name: String
let description: String
}
适配性与优势
1、自动适配:
在 iPhone:界面会以单栏堆栈导航形式展现。
在 iPad 和 macOS:界面会以多栏布局展现。
2、易用性:
使用状态绑定管理选中项。
分栏逻辑明确,开发复杂界面时更直观。
3、灵活性:
可以根据屏幕尺寸和使用场景自定义每栏的内容布局。
总结
NavigationSplitView 是构建多栏导航界面的核心组件,非常适合 iPad 和 macOS 应用程序。通过简单的绑定和分栏设置,可以快速实现复杂的多栏布局,同时提供良好的用户体验。
扩展知识
navigationSplitViewStyle修饰符
.navigationSplitViewStyle(.balanced)
示例代码:
NavigationSplitView(columnVisibility: $visibility) {
// Sidebar
List(0..<10, id: \.self) { item in
Text("Item \(item)")
}
.navigationTitle("Sidebar")
} detail: {
// Detail View
Text("Detail View")
}
.navigationSplitViewStyle(.balanced) // 设置为平衡样式
支持的样式:
.automatic: 默认行为,根据设备类型自动调整样式。例如:
在 iPad 上会显示多列。
在 iPhone 上通常会以单列显示。。
.balanced: 平衡布局,各列(Sidebar 和 Content 或 Detail)的宽度尽可能平衡分配。
.prominentDetail: 优化 Detail 列的显示,确保其在多列布局中占据更多空间。
NavigationSplitView和NavigationStack
可以在同一个应用中同时使用 NavigationSplitView 和 NavigationStack,从而实现跨设备的最佳用户体验。例如:
1、iPad 上使用 NavigationSplitView
显示多栏布局,提升大屏幕设备的生产力。
2、iPhone 上使用 NavigationStack
提供堆栈式的线性导航体验,更适合小屏幕用户。
代码示例:
struct ContentView: View {
@State private var selectedItem: String?
var body: some View {
if UIDevice.current.userInterfaceIdiom == .pad {
// 使用 NavigationSplitView 在 iPad 上显示多栏布局
NavigationSplitView {
List(["Item 1", "Item 2", "Item 3"], id: \.self, selection: $selectedItem) { item in
Text(item)
}
.navigationTitle("Sidebar")
} content: {
if let selectedItem = selectedItem {
Text("Content for \(selectedItem)")
} else {
Text("Select an item")
}
} detail: {
if let selectedItem = selectedItem {
Text("Details for \(selectedItem)")
} else {
Text("No item selected")
}
}
} else {
// 使用 NavigationStack 在 iPhone 上显示堆栈式导航
NavigationStack {
List(["Item 1", "Item 2", "Item 3"], id: \.self) { item in
NavigationLink(item, value: item)
}
.navigationDestination(for: String.self) { item in
Text("Details for \(item)")
}
.navigationTitle("Items")
}
}
}
}
NavigationSplitView 和 NavigationStack 各有优势,适用的设备和导航需求不同。
在 iPad 或 macOS 应用中,NavigationSplitView 是主流选择,提升多任务效率。
在 iPhone 上,NavigationStack 更适合单层级的线性导航。
通过适配判断(如设备类型或屏幕尺寸),两者可以结合使用以实现最佳用户体验。