Highlight
iOS 16 和 iPadOS 16 为 SwiftUI 工具栏带来了 Mac 级别的能力:overflow 菜单自动折叠、secondary action 放置、用户可自定义工具栏、ControlGroup 分组、可编辑导航标题和文档关联,让 iPad 应用的工具栏终于不再只是 iPhone 的放大版。
核心内容
从手动 Menu 到自动 Overflow
(02:05)
很多开发者在 iPhone 上习惯用一个 “More” 菜单来收纳工具栏按钮。在 iPad 的大屏幕上,这种写法浪费了可用空间。
iOS 16 的解决方案是 overflow 菜单:系统会自动把放不下的按钮收进菜单,不需要你手动写 Menu。
把 ToolbarItem 改成 ToolbarItemGroup,去掉手动包裹的 Menu,把按钮直接作为 group 的内容:
PlaceDetailContent(place: $place)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
FavoriteToggle(place: $place)
AdjustImageButton(place: $place)
AdjustMapButton(place: $place)
}
}
关键点:
ToolbarItemGroup会为每个子视图创建独立的工具栏项- 在 iPad 和 Mac 上,系统会自动把放不下的项移入 overflow 菜单
- 不需要手动维护 Menu 的展开逻辑
Secondary Action 和 ToolbarRole
(04:05)
导航栏有三个区域:leading(左侧)、center(中间)、trailing(右侧)。leading 和 trailing 放控件,center 放导航标题。
iOS 16 新增了 .secondaryAction placement。这类操作使用频率不高,但值得单独展示。默认情况下它们不会直接显示在工具栏上,而是进入 overflow 菜单。
用 toolbarRole(.editor) 改变布局行为:
PlaceDetailContent(place: $place)
.toolbar {
ToolbarItemGroup(placement: .secondaryAction) {
FavoriteToggle(place: $place)
AdjustImageButton(place: $place)
AdjustMapButton(place: $place)
}
}
.toolbarRole(.editor)
关键点:
.editor角色告诉导航栏”这个页面用于编辑内容”- 导航栏会把标题从 center 移到 leading,腾出 center 区域给 secondary action
- 在 compact size class(如 iPhone 竖屏)上,标题仍保持在 center
用户可自定义工具栏
(05:37)
macOS 长期支持工具栏自定义,iPadOS 16 也引入了这项能力。
启用自定义需要三个条件:
PlaceDetailContent(place: $place)
.toolbar(id: "place") {
ToolbarItem(id: "favorite", placement: .secondaryAction) {
FavoriteToggle(place: $place)
}
ToolbarItem(id: "image", placement: .secondaryAction) {
AdjustImageButton(place: $place)
}
ToolbarItem(id: "map", placement: .secondaryAction) {
AdjustMapButton(place: $place)
}
}
.toolbarRole(.editor)
关键点:
- 每个
ToolbarItem必须有唯一的id,且跨启动保持一致 toolbarmodifier 本身也需要一个id- 只有
ToolbarItem支持自定义,ToolbarItemGroup不支持 - iPad 上只有 secondary action 支持自定义,primary action 不支持
默认隐藏的按钮
(06:59)
有些操作对特定用户群体有用,但不应该默认显示。用 showsByDefault: false 让这些按钮初始时待在自定义面板里:
ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) {
ShareLink(item: place)
}
用户可以从自定义面板拖拽这类按钮到工具栏上。
ControlGroup 分组
(07:45)
相关的按钮可以用 ControlGroup 组合在一起,作为一个整体添加或移除:
ToolbarItem(id: "image", placement: .secondaryAction) {
ControlGroup {
AdjustImageButton(place: $place)
AdjustMapButton(place: $place)
} label: {
Label("Edits", systemImage: "wand.and.stars")
}
}
关键点:
- 提供
label后,当空间不足时整个组会折叠成一个菜单 - 没有
label时,按钮在空间不足时会逐个进入 overflow 菜单 - 用户可以把整个 ControlGroup 作为一个单元添加或移除
详细内容
完整的工具栏配置示例
PlaceDetailContent(place: $place)
.toolbar(id: "place") {
ToolbarItem(id: "new", placement: .primaryAction) {
NewButton()
}
ToolbarItem(id: "favorite", placement: .secondaryAction) {
FavoriteToggle(place: $place)
}
ToolbarItem(id: "image", placement: .secondaryAction) {
ControlGroup {
AdjustImageButton(place: $place)
AdjustMapButton(place: $place)
} label: {
Label("Edits", systemImage: "wand.and.stars")
}
}
ToolbarItem(id: "share", placement: .secondaryAction, showsByDefault: false) {
ShareLink(item: place)
}
}
.toolbarRole(.editor)
关键点:
id: "new"用primaryAction,固定在 trailing 区域,不可自定义id: "favorite"和id: "image"用secondaryAction,默认显示在 center 区域,可自定义id: "share"用showsByDefault: false,初始隐藏在自定义面板中- 所有 id 必须唯一且跨启动保持一致,因为 SwiftUI 用 id 持久化用户的自定义配置
导航标题菜单
(10:44)
导航标题现在支持菜单,类似于 macOS 的 File 菜单:
PlaceDetailContent(place: $place)
.navigationTitle(place.name) {
MyPrintButton()
}
标题右侧会出现菜单指示器,点击后弹出包含你提供的操作的菜单。
可编辑的导航标题
(11:07)
传入 Binding 告诉系统这个标题支持编辑:
PlaceDetailContent(place: $place)
.navigationTitle($place.name) {
MyPrintButton()
RenameButton()
}
关键点:
$place.name传入 Binding,系统知道标题可编辑RenameButton()触发重命名流程- 点击 RenameButton 后,标题变成可编辑的文本框
关联文档
(11:37)
把文档关联到视图,标题菜单会显示文档预览头:
PlaceDetailContent(place: $place)
.navigationTitle($place.name) {
MyPrintButton()
RenameButton()
}
.navigationDocument(place.url)
关键点:
- 标题菜单会显示文档预览,支持拖拽
- 提供 URL 时,macOS 上还会自动配置窗口工具栏的代理图标
- 参数可以是符合
Transferable的类型,也可以直接传 URL
核心启发
1. 给笔记 App 加可自定义工具栏
- 做什么:让用户决定工具栏上显示哪些格式化按钮
- 为什么值得做:不同用户的使用习惯差异很大,有人常用粗体,有人常用代码块,自定义工具栏让每个人都能有自己的工作流
- 怎么开始:用
toolbar(id:)+ToolbarItem(id:placement:)给每个按钮分配唯一 id,把不常用的按钮设showsByDefault: false
2. 给图片编辑 App 加 ControlGroup 工具栏
- 做什么:把旋转、裁剪、滤镜等操作按功能分组
- 为什么值得做:相关操作视觉上归为一组,用户更容易找到,空间不足时整组折叠成菜单
- 怎么开始:用
ControlGroup包裹相关按钮,给 group 加label让它在空间不足时折叠成菜单
3. 给文件管理 App 加文档标题菜单
- 做什么:点击导航标题弹出文件操作菜单(重命名、移动、分享)
- 为什么值得做:标题是用户视线自然停留的位置,把常用文件操作放在这里比藏在工具栏更直观
- 怎么开始:用
.navigationTitle($document.name) { ... }给标题加菜单,传入 Binding 支持重命名
4. 给 iPad 阅读 App 优化工具栏布局
- 做什么:用
toolbarRole(.editor)让 secondary action 占据 center 区域 - 为什么值得做:iPad 屏幕宽度足够,把书签、目录、字体设置等按钮放在 center 比全塞进 overflow 菜单更易于访问
- 怎么开始:把阅读相关的操作标记为
.secondaryAction,加上.toolbarRole(.editor),系统会自动把标题移到左侧腾出空间
关联 Session
- SwiftUI on iPad: Organize your interface — iPad 界面组织与列表、表格、分栏视图
- What’s new in SwiftUI — SwiftUI 年度新特性概览
- Meet Transferable — SwiftUI 数据传输协议,ShareLink 的基础
评论
GitHub Issues · utterances