关于 toolbars
面向 SwiftUI 开发者的工具栏模式指南,介绍自定义工具栏、搜索集成、系统项目位置和过渡效果。还涵盖 iOS、iPadOS 与 macOS 的平台差异及 API 版本要求。
主要功能
- 01添加可自定义工具栏项目
- 02配置搜索栏行为与位置
- 03调整系统工具栏项目位置
- 04创建工具栏过渡效果
- 05参考平台差异与 API 版本
适用场景
- 01为应用添加用户可配置的工具栏
- 02将搜索栏移至合适位置
- 03为工具栏按钮添加缩放过渡
- 04检查跨平台工具栏布局
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill toolbars安装后在 Claude Code 中输入/toolbars确认已加载。
详细介绍
SwiftUI 工具栏
适用于 SwiftUI 应用的现代工具栏模式。涵盖可自定义工具栏、增强的搜索集成、新的放置位置、过渡效果以及平台特定注意事项。
此技能的触发条件
当用户:
- 想要添加或自定义工具栏时
- 询问可自定义/用户可配置的工具栏时
- 需要在工具栏的特定位置放置搜索字段时
- 想要工具栏项目过渡或动画时
- 询问工具栏放置位置(bottomBar、largeSubtitle 等)时
- 需要了解平台特定的工具栏行为(iOS 与 macOS)时
- 想要重新定位系统工具栏项目(搜索、侧边栏)时
决策树
What toolbar feature do you need?
|
+- User-customizable toolbar (add/remove/reorder items)
| +- Use .toolbar(id:) with ToolbarItem(id:)
|
+- Search field in toolbar
| +- Minimize to button -> .searchToolbarBehavior(.minimize)
| +- Reposition search -> DefaultToolbarItem(kind: .search, placement:)
|
+- Toolbar transition/animation
| +- Zoom transition from toolbar item -> .matchedTransitionSource(id:in:)
| +- Hide glass background -> .sharedBackgroundVisibility(.hidden)
|
+- Custom subtitle area content
| +- Use ToolbarItem(placement: .largeSubtitle)
|
+- System toolbar items with custom placement
| +- DefaultToolbarItem(kind: .search/.sidebar, placement:)
API 可用性
| API | 最低版本 | 备注 |
|---|---|---|
.toolbar { } | iOS 14 | 基本工具栏 |
ToolbarItem(placement:) | iOS 14 | 标准放置位置 |
.toolbar(id:) | iOS 16 | 可自定义工具栏 |
ToolbarItem(id:) | iOS 16 | 可自定义工具栏中的项目 |
ToolbarSpacer | iOS 16 | 固定和灵活间隔器 |
.searchable() | iOS 15 | 搜索集成 |
.searchToolbarBehavior(.minimize) | iOS 17 | 最小化搜索按钮 |
DefaultToolbarItem(kind:placement:) | iOS 18 | 重新定位系统项目 |
ToolbarItem(placement: .largeSubtitle) | iOS 18 | 副标题区域内容 |
.matchedTransitionSource(id:in:) | iOS 18 | 工具栏过渡源 |
.sharedBackgroundVisibility() | iOS 18 | 控制玻璃背景 |
可自定义工具栏
允许用户通过添加、移除和重新排列工具栏项目来自定义工具栏:
ContentView()
.toolbar(id: "main-toolbar") {
ToolbarItem(id: "tag") {
TagButton()
}
ToolbarItem(id: "share") {
ShareButton()
}
ToolbarSpacer(.fixed)
ToolbarItem(id: "more") {
MoreButton()
}
}
工具栏间隔器
ToolbarSpacer(.fixed) // Fixed-width space
ToolbarSpacer(.flexible) // Flexible space — pushes items apart
反模式
// ❌ Missing IDs in customizable toolbar — items can't be customized
.toolbar(id: "main") {
ToolbarItem { // No id parameter
ShareButton()
}
}
// ✅ Every item needs its own ID
.toolbar(id: "main") {
ToolbarItem(id: "share") {
ShareButton()
}
}
增强的搜索集成
最小化搜索
将搜索字段显示为紧凑按钮,轻点后展开:
@State private var searchText = ""
NavigationStack {
RecipeList()
.searchable(text: $searchText)
.searchToolbarBehavior(.minimize)
}
重新定位搜索
将默认搜索字段移动到工具栏中的其他位置:
NavigationSplitView {
AllCalendarsView()
} detail: {
SelectedCalendarView()
.searchable(text: $query)
.toolbar {
ToolbarItem(placement: .bottomBar) {
CalendarPicker()
}
ToolbarItem(placement: .bottomBar) {
Invites()
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
NewEventButton()
}
}
}
系统定义的工具栏项目
使用自定义放置位置重新定位系统项目:
.toolbar {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
DefaultToolbarItem(kind: .sidebar, placement: .navigationBarLeading)
}
Large Subtitle 放置位置
在导航栏副标题区域放置自定义内容:
NavigationStack {
DetailView()
.navigationTitle("Title")
.navigationSubtitle("Subtitle")
.toolbar {
ToolbarItem(placement: .largeSubtitle) {
CustomLargeNavigationSubtitle()
}
}
}
过渡效果
从工具栏开始的匹配过渡
创建从工具栏按钮发起的缩放过渡:
struct ContentView: View {
@State private var isPresented = false
@Namespace private var namespace
var body: some View {
NavigationStack {
DetailView()
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Show Sheet", systemImage: "globe") {
isPresented = true
}
}
.matchedTransitionSource(id: "world", in: namespace)
}
.sheet(isPresented: $isPresented) {
SheetView()
.navigationTransition(
.zoom(sourceID: "world", in: namespace))
}
}
}
}
玻璃背景控制
控制工具栏项目共享的玻璃背景:
ContentView()
.toolbar(id: "main") {
ToolbarItem(id: "build-status", placement: .principal) {
BuildStatus()
}
.sharedBackgroundVisibility(.hidden)
}
最常见的 5 个错误
| # | 错误 | 修正方法 |
|---|---|---|
| 1 | 可自定义工具栏中的 ToolbarItem 缺少 id | .toolbar(id:) 中的每个项目都必须有自己的 id 参数 |
| 2 | 不使用 .searchable() 却使用 .searchToolbarBehavior(.minimize) | 必须与 .searchable() 修饰符配合使用——.minimize 只影响显示方式 |
| 3 | 将 .matchedTransitionSource 添加到 Button 而不是 ToolbarItem 上 | 在 ToolbarItem 上应用 .matchedTransitionSource(id:in:),而不是添加到其内容上 |
| 4 | 同时使用 .largeSubtitle 和 .navigationSubtitle(),并期望两者都显示 | .largeSubtitle 优先级更高——副标题修饰符的值会被隐藏 |
| 5 | 忘记为 DefaultToolbarItem 指定 placement: | 未显式指定时,系统项目会使用其默认位置 |
平台注意事项
| 平台 | 建议 |
|---|---|
| iOS | 底部栏在 iPhone 上很实用。使用 .searchToolbarBehavior(.minimize) 提高空间利用率。 |
| iPadOS | 可自定义工具栏对效率类应用很有价值。可考虑添加键盘快捷键。 |
| macOS | 用户期望工具栏可自定义。使用间隔器对项目进行逻辑分组。 |
检查清单
可自定义工具栏
-
.toolbar(id:)使用唯一且稳定的字符串标识符 - 其中的每个
ToolbarItem都有自己的唯一id - 使用间隔器对相关项目进行逻辑分组
- 使用自定义面板测试工具栏(在 iPadOS 上长按,在 macOS 上右键单击)
搜索集成
- 将
.searchable()与适当的.searchToolbarBehavior()配合使用 - 搜索位置应适合平台(iOS 上为底部栏,macOS 上为工具栏)
- 需要调整位置时,使用
DefaultToolbarItem(kind: .search)
转场
- 将
.matchedTransitionSource应用于ToolbarItem,而不是其内容视图 - 在视图层级使用
@Namespace声明命名空间 -
.matchedTransitionSource与.navigationTransition(.zoom)中的源 ID 保持一致
平台
- 在 iOS 和 macOS 上测试工具栏布局(如果是多平台应用)
- 底部栏项应适合小屏幕
- 为复杂的 macOS 应用提供可自定义工具栏
参考资料
评论
支持 Markdown