mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-19 13:35:54 +00:00
4.7 KiB
4.7 KiB
SwiftUI Toolbar Features
Quick Reference
| Feature | Modifier/Type | Purpose |
|---|---|---|
.toolbar(id:) |
Customizable toolbar | User can add/remove/reorder |
ToolbarSpacer |
Spacing control | Fixed or flexible spacing |
.searchToolbarBehavior() |
Search field display | Minimize/expand behavior |
DefaultToolbarItem |
System items | Reposition system controls |
.matchedTransitionSource() |
Transitions | Zoom from toolbar items |
Customizable Toolbars
Basic Setup
.toolbar(id: "main") {
ToolbarItem(id: "save") { SaveButton() }
ToolbarItem(id: "share") { ShareButton() }
ToolbarSpacer(.flexible)
ToolbarItem(id: "more") { MoreButton() }
}
Spacer Types
ToolbarSpacer(.fixed) // Fixed width
ToolbarSpacer(.flexible) // Pushes items apart
Search Integration
Minimize Behavior
@State private var searchText = ""
NavigationStack {
ContentView()
.searchable($searchText)
.searchToolbarBehavior(.minimize) // Compact search button
}
Repositioning Search
.toolbar {
ToolbarItem(placement: .bottomBar) { Button1() }
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarItem(placement: .bottomBar) { Button2() }
}
Placement Options
Common Placements
.toolbar {
// Navigation bar
ToolbarItem(placement: .navigationBarLeading) { }
ToolbarItem(placement: .navigationBarTrailing) { }
ToolbarItem(placement: .principal) { }
// Bottom bar (iOS)
ToolbarItem(placement: .bottomBar) { }
// Large title area
ToolbarItem(placement: .largeSubtitle) { CustomSubtitle() }
}
Large Subtitle
NavigationStack {
Content()
.navigationTitle("Title")
.navigationSubtitle("Subtitle")
.toolbar {
ToolbarItem(placement: .largeSubtitle) {
// Overrides navigationSubtitle
CustomSubtitleView()
}
}
}
Visual Effects
Matched Transitions
@State private var showDetail = false
@Namespace private var namespace
NavigationStack {
Content()
.toolbar {
ToolbarItem {
Button("Open") { showDetail = true }
.matchedTransitionSource(id: "btn", in: namespace)
}
}
.sheet(isPresented: $showDetail) {
DetailView()
.navigationTransition(.zoom(sourceID: "btn", in: namespace))
}
}
Background Visibility
.toolbar(id: "main") {
ToolbarItem(id: "status", placement: .principal) {
StatusView()
.sharedBackgroundVisibility(.hidden) // No glass background
}
}
System Items
Default Items
.toolbar {
// Reposition system search
DefaultToolbarItem(kind: .search, placement: .bottomBar)
// Sidebar toggle
DefaultToolbarItem(kind: .sidebar, placement: .navigationBarLeading)
}
Platform Considerations
iOS/iPadOS
#if os(iOS)
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
// Bottom bar items for iPhone
}
}
#endif
macOS
#if os(macOS)
.toolbar {
ToolbarItem(placement: .automatic) {
// macOS toolbar items
}
}
#endif
Common Patterns
Dynamic Toolbar
@State private var isEditing = false
.toolbar {
if isEditing {
ToolbarItem(id: "done") { DoneButton() }
} else {
ToolbarItem(id: "edit") { EditButton() }
}
}
Grouped Actions
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button("One") { }
Button("Two") { }
Button("Three") { }
}
}
Contextual Items
@State private var selection: Item?
.toolbar {
if selection != nil {
ToolbarItem { DeleteButton() }
ToolbarItem { ShareButton() }
}
}
Best Practices
- Unique IDs: Use meaningful identifiers for customizable items
- Logical Groups: Use spacers to group related actions
- Platform Awareness: Test on all target platforms
- Consistent Placement: Follow platform conventions
- Minimal Items: Avoid overcrowding toolbars
Troubleshooting
| Issue | Solution |
|---|---|
| Items not customizable | Add id to toolbar and items |
| Search not minimizing | Apply .searchToolbarBehavior(.minimize) |
| Transition not working | Check namespace and ID match |
| Items hidden | Check placement compatibility |