Pattern · Tab Bars
NewTop Tabs SwiftUI Block
A scrolling strip of top tabs over swipeable pages, its indicator tracking the swipe and the strip keeping the selected tab in view.
A strip of tabs over a pager of your pages. The accent indicator follows the pager continuously, stretching to each tab's width, tapping a tab pages to it, and the strip scrolls to keep the selected tab in the middle. Tabs can show counts, and three or fewer share the width while more scroll.
- SwiftUI
- iOS 17.0+
- top-tabs
$199 once, every piece included. See how installing works · Sign in
01
What's included
SwiftUI source you own, built on Apple frameworks only.
- An indicator that tracks the swipe and stretches to each tab's width
- Tap a tab to page to it, the strip scrolling to keep it centred
- Counts on tabs, in a quiet capsule
- Few tabs fill the width, many tabs scroll
- Generic over your own tab type, with lazily built pages
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
A strip of tabs sits over a hairline, and a pager of your pages sits under it. A thin accent indicator under the strip follows the pager as it moves: drag a page halfway and the indicator is halfway to the next tab, stretching or shrinking to its width. Tapping a tab pages straight to it, the indicator gliding along, and the strip scrolls to bring the selected tab to the middle. Tabs can show a count in a small capsule after the title. Three tabs or fewer share the strip's width equally; with more, each tab takes its own width and the strip scrolls. The selected title is textPrimary and the rest textSecondary. Pages are built lazily as they come near.
Notes
TopTabs(selection:items:sizing:page:)is generic over your own tab type.pagebuilds the page for a tab; each page may hold its own verticalScrollView.- The block owns its paging container, a horizontal
ScrollViewwith.scrollTargetBehavior(.paging)and.scrollPosition(id:), so give it the height it should fill: under an inline navigation title, or in a sheet. It brings no background or padding. TopTabsItem(_:title:count:)(alsoTopTabs.Item) is a tab: your value, a one-line title and an optional count. 0 still shows "0"; nil hides the capsule.sizingis.automatic(fill with three tabs or fewer, scroll with more),.fillor.scroll.- Set
selectionfrom your own code and the pager pages there. A swipe setsselectiononce the page settles. - The indicator's place is worked out from the pager's offset and each tab's measured width, so it tracks the finger at any speed and in either direction.
- Haptics: selection when the tab changes.
- VoiceOver reads each tab as a button with its count and the selected trait; only the current page is exposed. Tab titles grow with Dynamic Type and the strip scrolls to fit them.
- Reduce Motion pages without the slide when a tab is tapped.
TopTabsExampleat the end ofTopTabs.swiftis a reading app's feed with nine tabs (Following with 12, Events with 3), each page a card of articles. The file's "Few tabs" preview shows three tabs filling the width.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - Titles are
type.subheadat medium weight, intextPrimarywhen selected andtextSecondaryotherwise. Counts aretype.captionwith monospaced digits on afillcapsule. - The indicator is the block's one accent: a 2 point
accentcapsule. AnSPHairlineinseparatorruns under the strip. - Paging and the strip's scroll use
motion.smooth; title colours change withmotion.snappy.
03
Keep building
Pieces that work well next to this one.