SwiftUI New Toolbar Features
Overview
SwiftUI has introduced significant enhancements to its toolbar system, providing developers with more flexibility, customization options, and improved user experiences. These new features enable the creation of more sophisticated and interactive toolbars across Apple platforms, including iOS, iPadOS, and macOS. Key improvements include customizable toolbars, enhanced search integration, new placement options, and transition effects.
Customizable Toolbars
Creating a Customizable Toolbar
SwiftUI now supports customizable toolbars that users can personalize by adding, removing, and rearranging items.
ContentView()
.toolbar(id: "main-toolbar") {
ToolbarItem(id: "tag") {
TagButton()
}
ToolbarItem(id: "share") {
ShareButton()
}
ToolbarSpacer(.fixed)
ToolbarItem(id: "more") {
MoreButton()
}
}The toolbar(id:) modifier creates a customizable toolbar with a unique identifier. Each item in a customizable toolbar must have its own ID.
Toolbar Spacers
Toolbar spacers create visual breaks between items and can be fixed or flexible.
ToolbarSpacer(.fixed) // Creates a fixed-width space
ToolbarSpacer(.flexible) // Creates a flexible space that pushes items apartSpacers are also customizable - users can add multiple copies of spacers from the customization panel if the toolbar supports it.
Enhanced Search Integration
Search Toolbar Behavior
Control how search fields appear and behave in toolbars:
@State private var searchText = ""
NavigationStack {
RecipeList()
.searchable($searchText)
.searchToolbarBehavior(.minimize)
}The .minimize behavior renders the search field as a button-like control that expands when tapped, optimizing space in the toolbar.
Repositioning Search Items
Reposition the default search item in the toolbar:
NavigationSplitView {
AllCalendarsView()
} detail: {
SelectedCalendarView()
.searchable($query)
.toolbar {
ToolbarItem(placement: .bottomBar) {
CalendarPicker()
}
ToolbarItem(placement: .bottomBar) {
Invites()
}
DefaultToolbarItem(kind: .search, placement: .bottomBar)
ToolbarSpacer(placement: .bottomBar)
ToolbarItem(placement: .bottomBar) {
NewEventButton()
}
}
}The DefaultToolbarItem with .search kind allows you to reposition the search field within the toolbar.
New Toolbar Item Placements
Large Subtitle Placement
Place custom content in the subtitle area of the navigation bar:
NavigationStack {
DetailView()
.navigationTitle("Title")
.navigationSubtitle("Subtitle")
.toolbar {
ToolbarItem(placement: .largeSubtitle) {
CustomLargeNavigationSubtitle()
}
}
}The .largeSubtitle placement takes precedence over the value provided to the navigationSubtitle(_:) modifier.
Visual Effects and Transitions
Matched Transition Source
Create smooth transitions between toolbar items and other views:
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))
}
}
}
}The matchedTransitionSource(id:in:) modifier identifies a toolbar item as the source of a navigation transition.
Shared Background Visibility
Control the glass background effect on toolbar items:
ContentView()
.toolbar(id: "main") {
ToolbarItem(id: "build-status", placement: .principal) {
BuildStatus()
}
.sharedBackgroundVisibility(.hidden)
}The sharedBackgroundVisibility(_:) modifier adjusts the visibility of the glass background effect, allowing items to stand out visually.
System-Defined Toolbar Items
Use system-defined toolbar items with custom placements:
.toolbar {
DefaultToolbarItem(kind: .search, placement: .bottomBar)
DefaultToolbarItem(kind: .sidebar, placement: .navigationBarLeading)
}The DefaultToolbarItem initializer creates system-defined toolbar items with specific placements, allowing you to leverage system functionality while controlling positioning.
Platform-Specific Considerations
iOS and iPadOS
- Bottom bar placement is particularly useful on iPhones
- Search minimization works well on smaller screens
- Consider using
.searchToolbarBehavior(.minimize)for better space utilization
macOS
- Customizable toolbars are particularly valuable for productivity apps
- Users expect to be able to customize toolbars in complex applications
- Consider toolbar spacers to create logical groupings of related items
Best Practices
- Use meaningful IDs for toolbar items in customizable toolbars
- Group related actions together with appropriate spacing
- Consider platform differences when designing toolbar layouts
- Use system-defined items when appropriate to maintain platform consistency
- Test toolbar customization to ensure a good user experience
- Use transitions thoughtfully to enhance the user experience without being distracting