Pattern · Navigation Bars

New

Greeting 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
Get SwiftPieces Pro

$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 one GreetingHeader.Profile and 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.Profile holds the name (for the initials and VoiceOver), an optional greetingName (the first word of name by default) and an optional avatarURL. A photo that is missing, loading or fails to load shows the initials. Profile.sample is Maya Lin.
  • status replaces the date line: "3 tasks due today". Nil shows the date ("Friday, October 23"). The greeting and the date refresh every minute.
  • unreadCount sets the badge; zero hides it.
  • collapse runs from 0 (expanded) to 1 (compact), and values between blend the two. Pass it from your own scroll tracking, or use greetingHeader(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 drives collapse from the scroll offset (onScrollGeometryChange) over collapseDistance points, 72 by default. On iOS 17 the pinned header stays expanded unless you pass collapse. Hide the navigation bar on that screen.
  • onProfile makes the avatar a button; nil leaves it a picture. onNotifications nil hides the bell; onSearch nil 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.
  • GreetingHeaderExample at the end of GreetingHeader.swift is 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. Pass wrapsInNavigationStack: false to push it in your own stack.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • The greeting is the one display moment: the brand's display face (type.face) at 30 points, type.displayWeight capped at semibold, in textPrimary. When the face isn't bundled it falls back to SF in type.figureDesign, or in type.textDesign when figures are monospaced. The compact greeting is type.headline, the date type.subhead in textSecondary, the search prompt type.body in textTertiary.
  • The bell, the compact search button and the search field sit on fill with a 1 point separator stroke on the field, at radius.md. Symbols are textPrimary in the buttons and textSecondary in the field.
  • The unread badge is the accent with accentOn figures in type.caption, ringed in bg: the block's only accent.
  • The avatar's initials are textSecondary on fill, ringed in separator.
  • greetingHeader puts the bar on bg and fades in an SPHairline under it. Changes use motion.smooth and motion.snappy, adapted for Reduce Motion.