跳到主要内容
知仓学习社ZHICANG

toolbars

Modern SwiftUI toolbar patterns including customizable toolbars, search integration, transition effects, and platform-specific behavior. Use when im…

不碰外部(只输出文字)无严重或高危命中rshankras/claude-code-apple-skills

它会碰到什么

扫了多少1 个文本文件,9 KB
它会碰到什么不碰外部(只输出文字)
命中总数0 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

SwiftUI Toolbars

Modern toolbar patterns for SwiftUI apps. Covers customizable toolbars, enhanced search integration, new placements, transition effects, and platform-specific considerations.

When This Skill Activates

Use this skill when the user:

  • Wants to add or customize toolbars
  • Asks about customizable/user-configurable toolbars
  • Needs search field in toolbar with specific placement
  • Wants toolbar item transitions or animations
  • Asks about toolbar placements (bottomBar, largeSubtitle, etc.)
  • Needs platform-specific toolbar behavior (iOS vs macOS)
  • Wants to reposition system toolbar items (search, sidebar)

Decision Tree

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 Availability

| API | Minimum Version | Notes |

|-----|----------------|-------|

| .toolbar { } | iOS 14 | Basic toolbar |

| ToolbarItem(placement:) | iOS 14 | Standard placements |

| .toolbar(id:) | iOS 16 | Customizable toolbars |

| ToolbarItem(id:) | iOS 16 | Items in customizable toolbars |

| ToolbarSpacer | iOS 16 | Fixed and flexible spacers |

| .searchable() | iOS 15 | Search integration |

| .searchToolbarBehavior(.minimize) | iOS 17 | Minimized search button |

| DefaultToolbarItem(kind:placement:) | iOS 18 | Reposition system items |

| ToolbarItem(placement: .largeSubtitle) | iOS 18 | Subtitle area content |

| .matchedTransitionSource(id:in:) | iOS 18 | Toolbar transition source |

| .sharedBackgroundVisibility() | iOS 18 | Glass background control |

Customizable Toolbars

Allow users to personalize toolbar items by adding, removing, and rearranging:

ContentView()
    .toolbar(id: "main-toolbar") {
        ToolbarItem(id: "tag") {
            TagButton()
        }
        ToolbarItem(id: "share") {
            ShareButton()
        }
        ToolbarSpacer(.fixed)
        ToolbarItem(id: "more") {
            MoreButton()
        }
    }

Toolbar Spacers

ToolbarSpacer(.fixed)      // Fixed-width space
ToolbarSpacer(.flexible)   // Flexible space — pushes items apart

Anti-Patterns

// ❌ 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()
    }
}

Enhanced Search Integration

Minimized Search

Renders search field as a compact button that expands on tap:

@State private var searchText = ""

NavigationStack {
    RecipeList()
        .searchable(text: $searchText)
        .searchToolbarBehavior(.minimize)
}

Repositioning Search

Move the default search field to a different toolbar position:

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()
            }
        }
}

System-Defined Toolbar Items

Reposition system items with custom placements:

.toolbar {
    DefaultToolbarItem(kind: .search, placement: .bottomBar)
    DefaultToolbarItem(kind: .sidebar, placement: .navigationBarLeading)
}

Large Subtitle Placement

Place custom content in the navigation bar subtitle area:

NavigationStack {
    DetailView()
        .navigationTitle("Title")
        .navigationSubtitle("Subtitle")
        .toolbar {
            ToolbarItem(placement: .largeSubtitle) {
                CustomLargeNavigationSubtitle()
            }
        }
}

Transition Effects

Matched Transition from Toolbar

Create zoom transitions originating from a toolbar button:

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))
                }
        }
    }
}

Glass Background Control

Control the shared glass background on toolbar items:

ContentView()
    .toolbar(id: "main") {
        ToolbarItem(id: "build-status", placement: .principal) {
            BuildStatus()
        }
        .sharedBackgroundVisibility(.hidden)
    }

Top 5 Mistakes

| # | Mistake | Fix |

|---|---------|-----|

| 1 | Missing id on ToolbarItem in customizable toolbar | Every item in .toolbar(id:) must have its own id parameter |

| 2 | Using .searchToolbarBehavior(.minimize) without .searchable() | Must pair with .searchable() modifier — .minimize only affects rendering |

| 3 | Putting .matchedTransitionSource on the Button instead of ToolbarItem | Apply .matchedTransitionSource(id:in:) on the ToolbarItem, not its content |

| 4 | Using .largeSubtitle alongside .navigationSubtitle() expecting both to show | .largeSubtitle takes precedence — the subtitle modifier value is hidden |

| 5 | Forgetting placement: on DefaultToolbarItem | Without explicit placement, system items use their default position |

Platform Considerations

| Platform | Recommendations |

|----------|----------------|

| iOS | Bottom bar useful on iPhones. Use .searchToolbarBehavior(.minimize) for space efficiency. |

| iPadOS | Customizable toolbars valuable in productivity apps. Consider keyboard shortcuts. |

| macOS | Users expect toolbar customization. Use spacers for logical groupings. |

Review Checklist

Customizable Toolbars

  • [ ] .toolbar(id:) has a unique, stable string identifier
  • [ ] Every ToolbarItem within has its own unique id
  • [ ] Spacers used to create logical groups of related items
  • [ ] Toolbar tested with customization panel (long-press on iPadOS, right-click on macOS)

Search Integration

  • [ ] .searchable() paired with appropriate .searchToolbarBehavior()
  • [ ] Search placement makes sense for the platform (bottom bar on iOS, toolbar on macOS)
  • [ ] DefaultToolbarItem(kind: .search) used when repositioning is needed

Transitions

  • [ ] .matchedTransitionSource applied to ToolbarItem, not its content view
  • [ ] Namespace declared with @Namespace at the view level
  • [ ] Source ID matches between .matchedTransitionSource and .navigationTransition(.zoom)

Platform

  • [ ] Toolbar layouts tested on both iOS and macOS (if multiplatform)
  • [ ] Bottom bar items appropriate for small screens
  • [ ] Customizable toolbars provided for complex macOS apps

References

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。