Select (Segmented)
A SwiftUI Picker with the segmented style, showing two to five mutually exclusive options inline where selecting one takes effect immediately, and which requires an accessibilityLabel matching its visible label plus accessibilityElement children contain to be named to VoiceOver.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when the user picks one value from a small fixed set of two to five options that all stay visible at once (e.g., "Day / Week / Month", "Red / Green / Blue"). Uses a SwiftUI
Pickerwith.pickerStyle(.segmented). - Use when the choice takes effect immediately and no confirmation step is needed.
- Use when each option label is short enough that all segments fit inline without truncation.
Try a different component when
- Do not use when there are more than five options, or the labels are too long to fit inline, or a compact single-value display is wanted (use
select.menu). - Do not use when the intent is to spin through values on a rotating wheel (use
select.wheel). - Do not use when the control is a single on/off setting (use
switch.basic, orbutton.togglefor an in-context toggle). - Do not use when the segments switch between top-level sections of the app; that is the job of a tab bar, not a selection control (use
tab-bar.basic).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- Use a native
Pickerwith.pickerStyle(.segmented)so the segments expose their selectable roles and current selection to VoiceOver and Switch Control. - Give the picker an
.accessibilityLabelthat matches its visible label text, AND apply.accessibilityElement(children: .contain)to the picker. With the segmented style, thePickerlabel text alone is not spoken; the label is announced only when both modifiers are present (WCAG 1.3.1). This is the opposite of the menu style inselect.menu, where an.accessibilityLabelis forbidden and thePickerlabel text serves as the name. - Provide a visible label for the group (the
Pickerlabel text or a precedingText) so a sighted user knows what the segments choose, and match the.accessibilityLabelto it (WCAG 3.3.2). - Each segment has clear, distinct text so the options are distinguishable and each
.tagmatches the selection type. - Meets the touch target size baseline in
global_rules.md(global.touch-target-size). - Meets the system focus indicator baseline in
global_rules.md(global.focus-visible).
Donts
Avoid these accessibility and UX barriers.
- Do not rely on the
Pickerlabel text alone to name a segmented picker; without.accessibilityElement(children: .contain), the.accessibilityLabelis not spoken and VoiceOver users hear only the segment text, never the group label. - Do not carry over the menu-style rule of omitting
.accessibilityLabel; the segmented style is the documented opposite and needs the label set. - Do not leave the group unlabeled (
Picker("", ...)with no.accessibilityLabel); the segments then have no group name. - Do not convey the selected segment by color alone; keep the native segmented style so the selection is shown by fill and shape, not only tint.
Customizable
Alternatives and options that give the AI agent some room to move.
- The current selection may also be shown in a separate visible
Text(e.g., "Fruit: Apple") in addition to the highlighted segment, as long as it stays in sync with the binding. - The option set may be static (a
ForEachover aCaseIterableenum) or dynamic, as long as each option has stable, distinct text and a.tagmatching the selection type.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
import SwiftUI
struct SelectSegmentedDemo: View {
enum Fruit: String, CaseIterable, Identifiable {
case apple = "Apple", banana = "Banana", cherry = "Cherry"
var id: Self { self }
}
@State private var fruit: Fruit = .apple
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// Visible group label for sighted users.
Text("Fruit")
Picker("Fruit", selection: $fruit) {
ForEach(Fruit.allCases) { fruit in
Text(fruit.rawValue).tag(fruit)
}
}
.pickerStyle(.segmented)
// Segmented style needs BOTH of these or the label is not spoken to VoiceOver;
// this is the opposite of the menu style, which forbids .accessibilityLabel.
.accessibilityElement(children: .contain)
.accessibilityLabel("Fruit")
.onChange(of: fruit) {
print("selection took effect immediately: \(fruit.rawValue)")
}
}
}
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Traits & semantics
- The picker is a container of selectable segments; each segment exposes its own label and the group carries the accessible name from the
.accessibilityLabel.
VoiceOver
- On first moving focus to a segment, VoiceOver speaks the group label (from the
.accessibilityLabel) together with the segment, which happens only when.accessibilityElement(children: .contain)is also present. - Removing either
.accessibilityElement(children: .contain)or the.accessibilityLabeldrops the group label, and VoiceOver announces only the segment text. - Selecting a segment updates the current selection, and the change takes effect immediately.
Switch Control & Full Keyboard Access
- Every segment is reachable and selectable via Switch Control and a hardware keyboard, and the selection updates immediately.
Dynamic Type
- The group label, the segment labels, and any accompanying value text scale with Dynamic Type and stay legible at accessibility text sizes.
Visual
- The selected segment is distinguishable from the others by the native segmented fill and shape, not by tint color alone.