Inspectors in SwiftUI: Discover the details
Swift & UI 进阶 20m

SwiftUI 中的 Inspector:展示内容细节的新方式

Inspectors in SwiftUI: Discover the details

2023年6月5日

在 Apple 官方观看视频

一句话判断

SwiftUI 的 inspector 修饰符为 macOS 和 iOS/iPadOS 提供了一个统一的”详情面板”抽象——在大屏上表现为尾随侧栏,在紧凑尺寸上自动适配为可调整大小的 Sheet。

这场 Session 讲了什么

Session 介绍了 SwiftUI 新增的 inspector 修饰符,它用于展示选中内容的详细信息。类似 Keynote 中右侧的格式检查器、Shortcuts 中的操作库面板。

基本用法inspector 修饰符接受一个 Bool 绑定(控制显示/隐藏)和一个 ViewBuilder(检查器的内容)。检查器默认使用 GroupedFormStyle,不需要手动设置表单样式。

列宽控制inspectorColumnWidth 修饰符可以设置最小、理想和最大宽度。理想宽度是首次启动时的尺寸,用户手动调整后系统会自动持久化。

上下文适配。检查器会根据放置位置自动选择样式:放在 NavigationStack 内时使用 “under toolbar” 样式(工具栏在检查器上方);放在导航结构外时使用 “full height” 样式(检查器占据完整高度)。在紧凑尺寸类(如 iPhone 竖屏)上,检查器自动适配为可调整大小的 Sheet。

工具栏位置。在检查器 ViewBuilder 内声明的工具栏项目会出现在检查器上方的工具栏中;在外部声明的则出现在主导航工具栏中。这个规则在检查器适配为 Sheet 时也适用——内部工具栏项目会跟随 Sheet。

Session 还简要介绍了 iOS 16.4 引入的 Sheet 展示自定义修饰符:presentationBackgroundpresentationContentInteractionpresentationCornerRadius 等。

值得深挖的点

inspector 与 NavigationSplitView 的配合:如果你在 NavigationSplitView 中使用 inspector,应该把 inspector 放在 detail 列的 ViewBuilder 中,或者完全放在导航结构外部。放在 sidebar 列中会导致意外的布局行为。

full height vs under toolbar 的选择:这个选择不是你来做的,而是由 inspector 修饰符的位置决定的。理解这个规则对设计正确的布局层级至关重要。

presentation 修饰符的通用性presentationBackgroundpresentationCornerRadius 等修饰符不仅适用于 Sheet,也适用于 Popover 和 Inspector。这是 SwiftUI 统一展示定制 API 的一部分。

代码片段

添加一个基本的 Inspector:

struct AnimalTableView: View {
    @State private var selectedAnimal: Animal?
    @State private var isInspectorPresented = false

    var body: some View {
        AnimalTable(selectedAnimal: $selectedAnimal)
            .inspector(isPresented: $isInspectorPresented) {
                // 检查器内容
                if let animal = selectedAnimal {
                    AnimalInspectorForm(animal: animal)
                        .inspectorColumnWidth(
                            min: 200, ideal: 300, max: 400
                        )
                        // 在检查器内部的工具栏项目
                        .toolbar {
                            ToolbarItem(placement: .confirmationAction) {
                                Button("完成") {
                                    isInspectorPresented = false
                                }
                            }
                        }
                }
            }
            // 在外部的工具栏项目出现在主导航工具栏
            .toolbar {
                ToolbarItem {
                    Button {
                        isInspectorPresented.toggle()
                    } label: {
                        Label("检查", systemImage: "info.circle")
                    }
                }
            }
    }
}

自定义 Sheet 的展示效果:

.sheet(isPresented: $showBulletin) {
    NibbleBulletinView(bulletin: bulletin)
        .presentationBackground(.thinMaterial)
        .presentationCornerRadius(20)
        // 允许背景内容交互
        .presentationContentInteraction(.scrolls)
}

最佳实践

  • 检查器放在 NavigationStack 内部或外部有不同效果,根据设计意图选择正确位置
  • 使用 inspectorColumnWidth 控制列宽,理想宽度作为首次展示尺寸
  • 在检查器 ViewBuilder 内声明的工具栏项目会出现在检查器上方
  • compact 尺寸类上检查器自动变成 Sheet,无需额外适配
  • 如果在 NavigationSplitView 中使用,放在 detail 列或导航结构外部

还有什么值得关注

  • Inspector 在 visionOS 上的表现和交互方式
  • presentationContentInteraction 对手势冲突的影响
  • Inspector 与 Sheet/Popover 的交互优先级
  • 自定义 presentation detents 与 Inspector 的组合使用
WWDC 2023