Pattern · Tab Bars
NewSearch Tab Bar SwiftUI Block
A floating tab bar with search set apart: tap it and the tabs fold away as it opens into a field over your recent searches.
The tabs sit in one floating capsule with the selected tab on a quiet pill, and a round search button stands beside it, the way iOS 26 apps place search. Tapping search folds the tabs into one round button showing the current tab and opens the button into a field with the keyboard up. The screen above shows recent searches, then your results as the query changes, and tapping the folded tab goes back.
- SwiftUI
- iOS 17.0+
- search-tab-bar
$199 once, every piece included. See how installing works · Sign in
01
What's included
SwiftUI source you own, built on Apple frameworks only.
- Tabs in a floating capsule, the selected one on a sliding pill
- Search set apart, opening into a field as the tabs fold away
- Recent searches with Clear, kept in your own store
- Your results view for the query, in place of the tab
- Badges, tap again to scroll to top, and tabs that keep their state
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
The tabs float in one capsule at the foot of the screen, each a symbol over its label, with the selected tab on a quiet pill that slides between them. A round search button stands apart beside the capsule, the way iOS 26 apps place search. Tapping it folds the tabs into a single round button showing the current tab and opens the search button into a field with the keyboard up. The screen above shows recent searches with Clear, and as the person types it shows your results for the query in place of the tab. Pressing Search, or picking a recent, adds the query to the front of the recents and calls your closure. Tapping the folded tab clears the field and returns to the tab. Tabs keep their content alive once visited, as TabView does, and tapping the selected tab again calls onReselect.
Notes
SearchTabBar(selection:items:query:prompt:recents:onSubmit:onReselect:content:results:)is generic over your own tab type (anyHashable & Sendablevalue, usually an enum).contentbuilds the screen for a tab andresultsbuilds the results for a trimmed, non-empty query.- A tab bar is the root of a screen, so this block owns its container rather than being a section: it lays your content out full size and floats the bar over its foot with
safeAreaInset(edge: .bottom), so your scroll views end above it. Put aNavigationStackinside each tab's content, not around the bar. SearchTabBarItem(_:title:symbol:badge:)is a tab: your value, its label, an outline SF Symbol (the selected tab shows its fill variant) and an optional count. Over 99 reads "99+"; nil or 0 hides it. Two to four tabs fit the capsule.queryandrecentsare bindings the app owns, so it can filter as the person types and keep recents across launches. Recents hold up to eight, newest first, without repeats. An empty list shows a short prompt instead.onSubmitruns on the main actor with the trimmed query. Filter inresultsas the query changes, and fetch or log inonSubmit.- Haptics: selection when the tab changes.
- VoiceOver reads each tab with its badge ("Library, 2 new") and the selected trait. The search button is "Search", and the folded tab reads "Back to Library". At accessibility sizes the labels step aside and a long press shows the system's large content viewer, as system tab bars do.
- Reduce Motion turns the fold and the opening field into short fades, and the press scale off.
SearchTabBarExampleat the end ofSearchTabBar.swiftis a listening app's root: Listen Now, Library with two new episodes and Browse, three recent searches, and results that filter a catalog of shows and episodes as you type.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - The capsule, the round buttons and the field are
color.surfacewith aseparatorhairline and thee2shadow. The selected tab's pill isfill. Tab labels aretype.captionat medium weight; symbols and labels aretextPrimarywhen selected andtextSecondaryotherwise. Badges aretextPrimarycapsules withsurfacefigures. - The field's text is
type.bodyintextPrimary, its symboltextSecondaryand its clear buttontextTertiary. The caret takes the app's tint, the block's one accent. - Recent searches sit on a
surfacecard atradius.lg, rows oftype.bodywithtextSecondarysymbols and inset hairlines, under atype.headlinetitle with Clear intype.subhead. Results and recents sit onbg. - The fold and the field open with
motion.standard; the clear button fades withmotion.snappy.
03
Keep building
Pieces that work well next to this one.