Pattern · Navigation Bars
NewSelection Toolbar SwiftUI Block
Edit mode for any list: a top bar with the count, Select all and Done, an action bar, selection circles on rows, and Undo.
Select mode for a list or grid. A top bar takes the navigation bar's place with the count, Select all and Done, rows gain selection circles, and an action bar of Share, Move, Duplicate and Delete enables by count. Actions run your async closures with working shown in place, Delete confirms with the count, and a line confirms each result with Undo.
- SwiftUI
- iOS 17.0+
- selection-toolbar
$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 generic selection model and a row modifier that adds the selection circle
- A top bar with a rolling count, Select all or Deselect all, and Done
- Share, Move, Duplicate and Delete that enable by count, or your own actions
- Actions show working in place; Delete confirms with the count
- A confirmation line with Undo after each action, or the error
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
While selecting, a top bar takes the place of the navigation bar: Select all or Deselect all, the count ("3 selected") rolling as you tap, and Done. Each row gains a selection circle that fills with the accent, and a tap anywhere on the row toggles it. An action bar rises from the bottom with Share, Move, Duplicate and Delete, or your own actions, each enabling once enough items are selected. An action runs your async closure with a spinner in place of its symbol while the other buttons rest; Delete first confirms with the count ("Delete 3 files?"). When it finishes, select mode ends and a line above the bottom edge confirms it ("Moved 3 files") with Undo for a few seconds; a thrown error shows its message there in the danger colour instead. Share opens the system share sheet with the links you return for the selection.
Notes
SelectionToolbarModel<ID>is the selection:isSelecting, theselectedset,count,begin(with:),end(),toggle(_:),select(_:),deselectAll()andkeep(only:)for after a reload. It is@Observable, so keep one in@Stateand share it with the toolbar and the rows.SelectionToolbar(_:all:noun:actions:content:)wraps your list or grid. Pass every id in order for Select all and the item's name in one and many (("file", "files")). The bars are overlays of the screen, so the toolbar adds them as safe area insets (your rows scroll clear of both), gives each its ground, and hides the navigation bar while selecting. Put your own Select button in the navigation bar and callbegin().selectionToolbarRow(_:in:)on a row adds the circle in select mode and makes the whole row a toggle; the row's own buttons rest while selecting.SelectionToolbarAction<ID>has ready-made.share { ids in urls },.move { ids in } undo: { ids in },.duplicate { }and.delete { } undo: { }, plusinit(id:title:symbol:role:minimum:done:endsSelection:perform:undo:)for your own:role: .destructivedraws it indangerand confirms first,minimumsets the count it needs,doneis the verb of its confirmation line ("Archived"), andendsSelectionleaves select mode on success.performandundoare@MainActorasync throwsand receive the selected ids. A thrownLocalizedErrorshows its description;CancellationError()backs out quietly. Withoutundo, the confirmation line has no Undo and Delete's dialog says it can't be undone.- Haptics tick on every selection change, mark success when an action finishes and an error when one fails.
- VoiceOver reads the count as the bar's header, each row's toggle as Select or Deselect with Selected on chosen rows, each action with "In progress" while it runs, and announces the confirmation line. The bars grow with Dynamic Type and every control has a 44 point target.
- Reduce Motion turns the bars' slide and the circles' entrance into short fades.
SelectionToolbarExampleat the end ofSelectionToolbar.swiftis the Files screen in the preview: Move that takes a moment and can be undone, Duplicate that adds copies, Delete that confirms and can be undone, and Share with links. PasswrapsInNavigationStack: falseto push it in your own stack.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - The top bar and the action bar sit on
bgwith anSPHairlineagainst the content. The count istype.headlinewith monospaced digits; Select all and Done aretype.body, Done semibold, intextPrimary. - Actions are an SF Symbol over a
type.captionlabel intextPrimary, ordangerfor destructive ones, at 35% when they can't run yet. - The selection circle is a
textTertiaryring, filling with theaccentand anaccentOncheck when chosen: the block's only accent. - The confirmation line is
surfaceElevatedwith aseparatorstroke atradius.md, asuccesscheck ordangermark,type.subheadtext and Undo in semiboldtextPrimary. - Changes use
motion.smoothandmotion.snappy, adapted for Reduce Motion.
03
Keep building
Pieces that work well next to this one.