The Business Case for Accessibility
15% of the population has a disability. Ignoring a11y ignores a huge market and opens you to lawsuits (ADA). It's also just the right thing to do. Accessibility (often abbreviated as a11y) is not optional—it's essential for inclusive design.
Market size:
- 1 billion people: Worldwide have some form of disability
- 15% of users: Potential customers you're excluding without a11y
- $8 trillion: Combined annual disposable income of people with disabilities
- Legal requirements: ADA (Americans with Disabilities Act) compliance mandatory in many regions
Business benefits:
- Increased Market Share: Reach more potential users
- Better UX for All: Accessible apps are better for everyone
- Legal Protection: Avoid costly lawsuits and penalties
- Brand Reputation: Shows commitment to inclusion
- SEO Benefits: Better semantic HTML improves search rankings
Accessibility isn't just for people with disabilities—it makes your app better for everyone.
VoiceOver Basics: Understanding Screen Reading
VoiceOver reads the screen to blind users. Navigation is linear (swipe left/right). Elements must be reachable and clear. VoiceOver is iOS's built-in screen reader:
How VoiceOver works:
- Linear Navigation: Users swipe left/right to move between elements
- Text-to-Speech: Reads element labels and content aloud
- Gesture Support: Double-tap to activate, swipe to navigate
- Rotor Navigation: Jump by headings, links, buttons, etc.
- Custom Gestures: Users configure preferred gestures
Key concepts:
- Accessibility Elements: What VoiceOver can read
- Accessibility Tree: The structure VoiceOver navigates
- Reading Order: The sequence in which elements are read
- Focus: Which element is currently selected
Understanding VoiceOver's linear navigation is crucial. Unlike sighted users who can see the entire screen, VoiceOver users experience your app sequentially. This makes logical ordering and clear labels essential.
Labels, Traits, and Hints: Making Elements Understandable
Label: "Play". Trait: "Button". Hint: "Double tap to start music". Configure these in Xcode Identity Inspector or code. These three properties make your UI accessible:
Accessibility Label:
- Purpose: Identifies the element to VoiceOver
- Best Practices: Be concise but descriptive
- Examples: "Play button", "Close menu", "Save profile"
- Code:
button.accessibilityLabel = "Play music"
Accessibility Traits:
- Purpose: Describes element type and behavior
- Common Traits: .button, .link, .header, .selected, .image
- Multiple Traits: Can combine traits (e.g., .button + .selected)
- Code:
button.accessibilityTraits = .button
Accessibility Hints:
- Purpose: Explains what happens when activated
- Best Practices: Only when the action isn't obvious
- Examples: "Double tap to open", "Swipe up to dismiss"
- Code:
button.accessibilityHint = "Double tap to start music"
In SwiftUI, use modifiers like .accessibilityLabel(), .accessibilityAddTraits(), and .accessibilityHint(). UIKit uses the accessibilityLabel, accessibilityTraits, and accessibilityHint properties.
Grouping Elements: Logical Content Organization
Don't make VoiceOver read "Song Title", swipe, "Artist", swipe. Group them so it reads "Song Title by Artist" in one go. Grouping related elements improves navigation efficiency:
Why grouping matters:
- Efficiency: Reduces number of swipes needed
- Context: Related information stays together
- Clarity: Makes content relationships clear
- Better UX: Faster navigation improves user experience
How to group:
- SwiftUI: Use
.accessibilityElement(children: .combine) - UIKit: Set
accessibilityElementsHiddenon parent, create custom label - Combine Labels: Merge related labels into one meaningful description
Example scenarios:
- List Items: Group title, subtitle, and metadata
- Cards: Combine image, title, and description
- Forms: Group label and input field
- Buttons: Group icon and text
Test your groupings: Enable VoiceOver and navigate your app. If you find yourself swiping through multiple elements that should be together, group them.
Custom Actions: Enhancing Complex Interactions
For complex items (e.g., mail list item), add "Rotor Actions" like Delete or Archive so users don't need to find tiny buttons. Custom actions make complex interfaces accessible:
When to use custom actions:
- Complex Items: List items with multiple actions
- Hidden Controls: Actions accessible via swipe
- Multiple Options: When one element has multiple functions
- Simplified Navigation: Reduce need for precise gestures
Implementing custom actions:
- SwiftUI: Use
.accessibilityAction(named: "Delete") - UIKit: Implement
accessibilityCustomActionsproperty - Actions Array: Provide array of UIAccessibilityCustomAction objects
Rotor navigation:
- Rotor: Accessibility feature to jump by element type
- Custom Rotors: Create custom categories (e.g., "Actions", "Links")
- Usage: Rotate two fingers on screen to change rotor category
Custom actions appear in VoiceOver's action menu, accessible via the rotor. This allows users to perform actions without navigating to tiny buttons, dramatically improving usability for complex interfaces.
Testing Accessibility: Ensuring Your App Works
Enable the Accessibility Inspector in Xcode. Or triple-click the side button on your iPhone and try to use your app with eyes closed. Testing is crucial for accessibility:
Accessibility Inspector (Xcode):
- Visual Tree: See the accessibility hierarchy
- Element Details: View labels, traits, hints for each element
- Warnings: Automatic detection of common issues
- Simulator Testing: Test VoiceOver in iOS Simulator
Device Testing:
- Enable VoiceOver: Settings → Accessibility → VoiceOver
- Physical Device: Best for real-world testing
- Blindfold Test: Try using your app with VoiceOver only
- Expert Users: Test with actual VoiceOver users
Automated Testing:
- XCUITest: Test accessibility properties programmatically
- Continuous Integration: Run accessibility tests in CI/CD
- Tools: Accessibility Scanner, axe DevTools
Testing with VoiceOver is eye-opening. You'll discover issues you never noticed visually. Make it part of your development workflow, not an afterthought.
Dynamic Type Support: Accommodating Different Vision Needs
Ensure your layouts adapt when fonts scale up. A11y isn't just for blind users; it's for low vision too. Dynamic Type is iOS's system-wide text scaling feature:
Why Dynamic Type matters:
- Low Vision Users: Need larger text to read comfortably
- Age-Related Vision: Vision changes with age
- Device Size: Users customize text size per device
- Legal Requirement: Required for accessibility compliance
Supporting Dynamic Type:
- Use System Fonts: SF Pro automatically scales
- Text Styles: Use .title, .body, .caption instead of fixed sizes
- Flexible Layouts: Designs must adapt to larger text
- Test Scaling: Check your app at all text size settings
SwiftUI implementation:
- Use
.font(.body)instead of fixed sizes - Leverage
@ScaledMetricfor spacing - Test with
.environment(.sizeCategory, .accessibilityExtraExtraLarge)
UIKit implementation:
- Use UIFont.preferredFont(forTextStyle:)
- Listen for UIContentSizeCategory.didChangeNotification
- Use constraint priorities for flexible layouts
Test your app at all Dynamic Type sizes, including the extra large accessibility sizes. Many apps break at larger sizes—don't let yours be one of them. Proper Dynamic Type support makes your app usable by millions more users.
Accessibility isn't a feature—it's a fundamental requirement for inclusive app design. Build it in from the start, not as an afterthought.