Pattern · Navigation Bars

New

Selection 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
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 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, the selected set, count, begin(with:), end(), toggle(_:), select(_:), deselectAll() and keep(only:) for after a reload. It is @Observable, so keep one in @State and 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 call begin().
  • 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: { }, plus init(id:title:symbol:role:minimum:done:endsSelection:perform:undo:) for your own: role: .destructive draws it in danger and confirms first, minimum sets the count it needs, done is the verb of its confirmation line ("Archived"), and endsSelection leaves select mode on success.
  • perform and undo are @MainActor async throws and receive the selected ids. A thrown LocalizedError shows its description; CancellationError() backs out quietly. Without undo, 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.
  • SelectionToolbarExample at the end of SelectionToolbar.swift is 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. Pass wrapsInNavigationStack: false to push it in your own stack.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • The top bar and the action bar sit on bg with an SPHairline against the content. The count is type.headline with monospaced digits; Select all and Done are type.body, Done semibold, in textPrimary.
  • Actions are an SF Symbol over a type.caption label in textPrimary, or danger for destructive ones, at 35% when they can't run yet.
  • The selection circle is a textTertiary ring, filling with the accent and an accentOn check when chosen: the block's only accent.
  • The confirmation line is surfaceElevated with a separator stroke at radius.md, a success check or danger mark, type.subhead text and Undo in semibold textPrimary.
  • Changes use motion.smooth and motion.snappy, adapted for Reduce Motion.