Switch
Native SwiftUI Toggle representing a persistent on/off setting. The label and switch state merge into a single accessible element.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when a control represents a persistent binary setting that remains on or off beyond the current interaction (e.g., "Enable notifications", "Dark mode"). Uses a native SwiftUI
Toggle, whose label and switch state merge into a single accessible element. - Use when the setting takes effect immediately when toggled, without requiring form submission.
- Use when the control reflects the current state of a system or application preference.
Try a different component when
- Do not use when the control triggers an in-place action or transient feature toggle within the current context (use
button.toggle). - Do not use when more than two states are required, or the state cycles through several values (use
button.toggle).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- Use the native
Togglecontrol rather than a custom composition, and pass the label directly into the initializer (e.g.,Toggle("Enable notifications", isOn: $binding)) so the label, switch role, and on/off state merge into a single accessible element. - The switch has an accessible name that describes the setting. Often this is worded to be true when the switch is on (e.g., "Enable notifications").
- When there is no unique visible label (e.g., repeated switches in a list sharing generic visual text), give each
Togglea unique, specific.accessibilityLabel. - When the on/off state is represented by visible value text other than "On"/"Off" (e.g., "Allowed"/"Blocked", "Dark"/"Light"), set
.accessibilityValueto match so VoiceOver announces the correct state instead of defaulting to generic On/Off wording. - The off-state fill maintains at least 3:1 contrast against the adjacent background, so the off state is distinguishable by more than color (a plain light gray is a common offender).
- 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 build a switch out of a custom
HStackofTextplus a shape-based knob without grouping it. Without an explicitToggle(or.accessibilityElement(children: .combine)), the label and the control become two disconnected accessibility elements instead of one combined name-and-state announcement. - Do not leave
.accessibilityValuemismatched with the visible value text when the switch shows custom state wording instead of On/Off. - Do not rely on color alone (e.g., green vs. gray) to convey on/off state; the off state must remain distinguishable through sufficient contrast.
Customizable
Alternatives and options that give the AI agent some room to move.
- A custom
.toggleStylemay restyle appearance (color, shape, knob position) as long as it continues to wrap a realToggleunderneath, preserving native semantics rather than rebuilding the control from scratch.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
import SwiftUI
struct SwitchBasicDemo: View {
@State private var notifications = false
@State private var displayMode = false
var body: some View {
VStack(alignment: .leading, spacing: 16) {
// Native Toggle: label, switch, and state merge into one accessible element
Toggle("Enable notifications", isOn: $notifications)
// Custom value text instead of On/Off; accessibilityValue must match
Toggle(isOn: $displayMode) {
Text("Display Mode")
}
.accessibilityValue(displayMode ? "Dark" : "Light")
}
}
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Traits & semantics
- The switch is a single accessible element carrying the switch trait, its name, and its on/off value; no separate static-text element duplicates the label.
VoiceOver
- VoiceOver speaks the setting name, the switch role, and the current state.
- When custom value wording is used, VoiceOver announces that wording (e.g., "Dark"/"Light"), not generic On/Off.
- Double-tapping flips the switch and the announced state updates.
- Repeated switches each speak a distinct name.
Switch Control & Full Keyboard Access
- The switch is reachable and togglable via Switch Control and a hardware keyboard, with the state change announced.
Dynamic Type
- The switch's label scales with Dynamic Type and stays fully visible at accessibility text sizes.
Visual
- The off state is distinguishable from on by more than color, with the off-state fill meeting 3:1 contrast against its background.