Pattern · Navigation Bars
NewGreeting Header SwiftUI Block
The top of a home tab: the date, a bell with its unread count, the avatar, a greeting by time of day and search, folding into a compact bar.
A home tab's header in place of a plain large title. Today's date, the notifications bell with an unread badge and the avatar sit on one row over a greeting for the time of day and a search field. As content scrolls the greeting and search fold away into a compact bar, driven by the scroll offset on iOS 18 or by any value you pass.
- SwiftUI
- iOS 17.0+
- greeting-header
$199 once, every piece included. See how installing works · Sign in
01
What's included
SwiftUI source you own, built on Apple frameworks only.
- A greeting for the time of day with the person's name, in the brand's display face
- The bell's unread badge rolls to each new count
- The avatar opens the profile, with initials when there is no photo
- Folds into a compact bar as content scrolls, with search as a button
- Pins over your scroll view with one modifier, and shows grey shapes while loading
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
One row holds today's date (or a status line you pass), the notifications bell with its unread count in the accent, and the person's avatar, a photo or their initials. Under it the greeting changes with the time of day ("Good morning, Maya") in the brand's display face, and a search field opens your search. As content scrolls, the greeting and the search field fold away and the row becomes a compact bar: the greeting moves into it in the headline style, search becomes a round button, the avatar shrinks a little, and a hairline appears under the bar. The badge rolls to each new count and reads "99+" past 99. Pass nil for the profile while it loads and the header shows grey shapes in its own layout.
Notes
GreetingHeader(profile:status:unreadCount:collapse:searchPrompt:onProfile:onNotifications:onSearch:)takes oneGreetingHeader.Profileand plain closures for the three ways out. On its own it is a section with no background, padding or scroll view, so it also sits at the top of any column.GreetingHeader.Profileholds thename(for the initials and VoiceOver), an optionalgreetingName(the first word ofnameby default) and an optionalavatarURL. A photo that is missing, loading or fails to load shows the initials.Profile.sampleis Maya Lin.statusreplaces the date line: "3 tasks due today". Nil shows the date ("Friday, October 23"). The greeting and the date refresh every minute.unreadCountsets the badge; zero hides it.collapseruns from 0 (expanded) to 1 (compact), and values between blend the two. Pass it from your own scroll tracking, or usegreetingHeader(collapseDistance:_:)on your scroll view: it pins the header under the status bar on the theme's ground with the screen margins, keeps the first row clear of it, and on iOS 18 drivescollapsefrom the scroll offset (onScrollGeometryChange) overcollapseDistancepoints, 72 by default. On iOS 17 the pinned header stays expanded unless you passcollapse. Hide the navigation bar on that screen.onProfilemakes the avatar a button; nil leaves it a picture.onNotificationsnil hides the bell;onSearchnil hides the field and the compact search button.- VoiceOver reads the greeting as the screen's header, the bell as "Notifications, 3 unread" and the avatar as "Profile, Maya Lin". Only the visible greeting is read as the header collapses. Every control has a 44 point target, the greeting wraps at large text sizes, and the field and buttons grow with Dynamic Type.
- Reduce Motion turns the loading shimmer off and the state changes into short fades.
GreetingHeaderExampleat the end ofGreetingHeader.swiftis the Home tab in the preview: the header pinned over Today and Recent cards, a notification that arrives a moment after launch, and a bell that clears the count. PasswrapsInNavigationStack: falseto push it in your own stack.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - The greeting is the one display moment: the brand's display face (
type.face) at 30 points,type.displayWeightcapped at semibold, intextPrimary. When the face isn't bundled it falls back to SF intype.figureDesign, or intype.textDesignwhen figures are monospaced. The compact greeting istype.headline, the datetype.subheadintextSecondary, the search prompttype.bodyintextTertiary. - The bell, the compact search button and the search field sit on
fillwith a 1 pointseparatorstroke on the field, atradius.md. Symbols aretextPrimaryin the buttons andtextSecondaryin the field. - The unread badge is the
accentwithaccentOnfigures intype.caption, ringed inbg: the block's only accent. - The avatar's initials are
textSecondaryonfill, ringed inseparator. greetingHeaderputs the bar onbgand fades in anSPHairlineunder it. Changes usemotion.smoothandmotion.snappy, adapted for Reduce Motion.
03
Keep building
Pieces that work well next to this one.