iOS Guide
iOS accessibility cheatsheet
A practical reference for making iPhone and iPad apps usable with VoiceOver, larger text, Switch Control, and system accessibility settings.
All materials
1. Why it matters
- Better UX for everyone: larger text, clearer focus, less motion sickness.
- App Store and Apple HIG expect solid accessibility basics.
- Many fixes are small API choices: labels, traits, and Dynamic Type.
2. Core APIs (UIKit + SwiftUI)
| Need | UIKit | SwiftUI |
| Accessible name | accessibilityLabel | .accessibilityLabel() |
| Hint / how to use | accessibilityHint | .accessibilityHint() |
| Value (slider, switch) | accessibilityValue | .accessibilityValue() |
| Role / behavior | accessibilityTraits | .accessibilityAddTraits() |
| Hide decoration | isAccessibilityElement = false | .accessibilityHidden(true) |
| Group children | container + combined elements | .accessibilityElement(children: .combine) |
| Custom actions | UIAccessibilityCustomAction | .accessibilityAction() |
3. VoiceOver labels that work
- Describe the control, not the visual style: "Delete message", not "Red trash icon".
- Keep labels short; put extra guidance in the hint.
- Do not hardcode visible text into the label if the UI label already speaks it - avoid double reading.
- For icons-only buttons, a label is mandatory.
- Update labels when state changes (Playing / Paused, Selected / Not selected).
// SwiftUI
Button(action: delete) {
Image(systemName: "trash")
}
.accessibilityLabel("Delete message")
.accessibilityHint("Removes this message from the chat")
// UIKit
button.accessibilityLabel = "Delete message"
button.accessibilityHint = "Removes this message from the chat"
button.accessibilityTraits = .button
4. Traits and roles
| Trait | Use when |
.button | Tappable action control |
.link | Opens another screen / URL |
.header | Section title for rotor navigation |
.selected | Current tab, chip, or selected cell |
.adjustable | Slider / stepper with swipe up-down |
.image | Meaningful image (not decorative) |
.updatesFrequently | Live values (timer, download %) |
Pick one clear role. Conflicting traits confuse VoiceOver.
5. Dynamic Type
- Use text styles (
body, headline, footnote), not fixed point sizes only.
- In UIKit:
label.adjustsFontForContentSizeCategory = true with a text style font.
- In SwiftUI: prefer
.font(.body) / semantic styles; test largest Accessibility sizes.
- Layouts must not clip or overlap at XXXL.
- Prefer wrapping over truncating critical text.
// UIKit
label.font = .preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 0
// SwiftUI
Text("Account settings")
.font(.title2)
.fixedSize(horizontal: false, vertical: true)
6. Motion, contrast, and system settings
| Setting | What to do |
| Reduce Motion | Replace large parallax/zooms with fades or no animation |
| Increase Contrast | Strengthen borders and text/background contrast |
| Bold Text | Do not rely on hairline fonts only |
| Differentiate Without Color | Do not use color alone for errors/states |
| Button Shapes | Ensure tappable text still looks actionable |
// SwiftUI Reduce Motion example
@Environment(\.accessibilityReduceMotion) var reduceMotion
withAnimation(reduceMotion ? nil : .easeInOut) {
isExpanded.toggle()
}
7. Focus order and grouping
- Reading order should match visual order (top-left to bottom-right in LTR).
- Group title + subtitle + meta into one element when they form one card.
- Hide decorative images and separators from VoiceOver.
- After modal dismiss / navigation, move focus to a sensible element.
- Custom components need a clear accessibility frame (hit target roughly 44x44 pt).
8. Quick checklist before release
| # | Check |
| 1 | Every icon button has a label |
| 2 | Forms announce errors, not only red color |
| 3 | Dynamic Type at largest sizes does not break UI |
| 4 | VoiceOver can complete main user flows |
| 5 | Reduce Motion has a calm alternative |
| 6 | Images that matter have descriptions; decorative ones are hidden |
| 7 | Selected states are exposed with traits/values |
| 8 | Sheet/dialog titles are announced as headers |
9. How to test on device
- Settings - Accessibility - VoiceOver - turn on (or use Accessibility Inspector on Mac).
- Practice gestures: swipe to move, double-tap to activate, two-finger scrub to go back.
- Settings - Display and Text Size - Larger Text - test max sizes.
- Enable Reduce Motion and Increase Contrast, then re-check key screens.
- For automation: XCTest accessibility audits / Accessibility Inspector warnings.