iOS Accessibility Best Practices: Reach More Users & Meet Compliance
Publish Date: 2026-05-10
Last Updated: 2026-05-10
Author: AppPreflight Team
Overview
Accessibility isn't just ethical—it's a business opportunity. 1 billion+ people worldwide have disabilities. Making your app accessible increases addressable market and often meets legal compliance requirements. Apple rewards accessible apps with better visibility. This guide covers implementing accessibility that benefits everyone.
1. Why Accessibility Matters
Market Opportunity
- 15-20% of global population has disabilities
- Older adults increasingly use apps
- Parents use voice control while hands busy
- Many temporary disabilities (broken arm, bright sunlight)
Legal Compliance
United States: Americans with Disabilities Act (ADA)
- Apps must be accessible to people with disabilities
- Applies if used as public accommodation
- Violations can result in lawsuits
European Union: European Accessibility Act
- Accessibility mandatory for mobile apps
- Technical specifications provided
- Fines for non-compliance
Other Regions
- Canada: AODA (Accessibility for Ontarians with Disabilities Act)
- Australia: Disability Discrimination Act
- Many countries have similar requirements
Apple's Preference
- Accessibility is part of App Review criteria
- Inaccessible apps may be rejected
- Apps with good accessibility get featured
- Accessibility impacts App Store ranking
2. Core Accessibility Principles
WCAG Guidelines (Web Content Accessibility Guidelines)
Apply to iOS apps:
1. Perceivable
- Content visible/audible to all users
- Not relying on color alone
- Captions for videos
2. Operable
- Keyboard navigation
- Touch targets large enough (44x44 pt minimum)
- No content trapped in specific input method
3. Understandable
- Clear language and instructions
- Consistent navigation
- Error messages helpful
4. Robust
- Compatible with assistive technologies
- Works with VoiceOver
- Supports dynamic text sizing
3. VoiceOver Implementation
What is VoiceOver
Screen reader for blind/low-vision users:
- Describes on-screen elements via audio
- Allows navigation via gestures
- Reads text, descriptions, labels
Enabling VoiceOver
Settings > Accessibility > VoiceOver > ON
Implementation in Code
// ❌ WRONG - No accessibility labels
Button(action: {}) {
Image(systemName: "checkmark")
}
// ✅ CORRECT - Clear accessibility label
Button(action: {}) {
Image(systemName: "checkmark")
.accessibility(label: Text("Mark as complete"))
}
// Better: Include hint if helpful
Button(action: {}) {
Image(systemName: "checkmark")
.accessibility(label: Text("Mark as complete"))
.accessibility(hint: Text("Mark this task as completed"))
}
UIKit Example
let button = UIButton()
button.accessibilityLabel = "Mark as complete"
button.accessibilityHint = "Mark this task as completed"
button.accessibilityTraits = .button
Custom Actions
VStack {
Text("Item")
.accessibility(customActions: [
AccessibilityCustomAction(
name: "Mark as complete",
target: self,
selector: #selector(markComplete)
),
AccessibilityCustomAction(
name: "Delete",
target: self,
selector: #selector(delete)
)
])
}
@objc func markComplete() { ... }
@objc func delete() { ... }
4. VoiceOver Testing
Testing on Device
1. Settings > Accessibility > VoiceOver > ON
2. Home button: Single tap (speak element)
3. Home button: Double tap (activate)
4. Swipe right: Next element
5. Swipe left: Previous element
Common Issues to Test For
☐ All interactive elements have labels
☐ Images have meaningful descriptions
☐ Status information conveyed in text (not just color/icon)
☐ Navigation order makes sense
☐ Form fields clearly labeled
☐ Buttons do what their label suggests
☐ No keyboard traps
☐ Text is readable with large fonts
☐ Links are distinguishable
5. Dynamic Type & Text Scaling
Support Dynamic Text Sizes
Apple's accessibility feature allows user-selected text size (from "Small" to "Accessibility Extra Large").
Implementation
// SwiftUI
Text("Hello")
.font(.body) // Automatically scales with system setting
.dynamicTypeSize(.large) // Optional: fix size if needed
// UIKit
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
Testing
Settings > Accessibility > Display & Text Size > Larger Accessibility Sizes
Test app at: Small, Medium, Large, Extra Large, + 3 Accessibility levels
Verify: Text readable, layout doesn't break, interactions work
6. Color & Contrast
Color Contrast Requirements
WCAG AA Standard (minimum)
- Normal text: 4.5:1 contrast ratio
- Large text (18pt+): 3:1 contrast ratio
Testing
Use Contrast Checker:
- Contrast Ratio (webachecker.com)
- Enter foreground & background colors
- Check against 4.5:1 or 3:1 standard
Don't Rely on Color Alone
// ❌ WRONG - Using color only
if error {
textField.backgroundColor = .red // Color alone
}
// ✅ CORRECT - Color + text + icon
if error {
textField.backgroundColor = .red
errorLabel.text = "Invalid email address"
errorIcon.image = UIImage(systemName: "exclamationmark.circle.fill")
}
Dark Mode Support
// Support both light and dark mode
extension UIColor {
static let appBackground = UIColor { traitCollection in
if traitCollection.userInterfaceStyle == .dark {
return UIColor.black
} else {
return UIColor.white
}
}
}
7. Touch Target Sizing
Minimum Touch Target Size
Apple recommendation: 44x44 points
// ❌ Too small (hard for users with motor difficulties)
Button(action: {}) {
Image(systemName: "plus")
.frame(width: 24, height: 24)
}
// ✅ Correct size
Button(action: {}) {
Image(systemName: "plus")
}
.frame(width: 44, height: 44)
.contentShape(Rectangle()) // Touch target extends to frame
Spacing Between Interactive Elements
Minimum: 8 points between adjacent buttons
// ❌ Buttons too close
HStack {
Button("Yes") { }
Button("No") { }
}
// ✅ Adequate spacing
HStack(spacing: 16) {
Button("Yes") { }
Button("No") { }
}
8. Keyboard Navigation
Full Keyboard Accessibility
Some users cannot use touch:
- VoiceOver with keyboard
- External keyboards
- Switch control users
// ✅ Enable keyboard navigation
TextField("Username", text: $username)
.textContentType(.username)
.keyboardType(.asciiCapable)
// Tab order
struct ContentView: View {
var body: some View {
VStack {
TextField("Email", text: $email)
.focusable()
.focused($focusedField, equals: .email)
SecureField("Password", text: $password)
.focusable()
.focused($focusedField, equals: .password)
Button("Login") { }
.focusable()
}
}
}
Test Keyboard Navigation
Settings > Accessibility > Keyboard > Full Keyboard Access > ON
Then press Tab to navigate through all interactive elements
Verify: All buttons/inputs reachable, logical order
9. Video & Audio Accessibility
Video Captions
Required for:
- All videos with audio
- Speech, dialogue, sound effects
- Music (if important to content)
// Add captions to AVPlayer
player.currentItem?.externalMetadata = [/* caption tracks */]
// In app assets
// MyVideo.mp4 + MyVideo.srt (subtitle file)
Audio Descriptions
For users who cannot see:
- Separate audio track describing visual content
- Integrated into main audio
- Option to enable/disable
10. Testing Accessibility
Built-in Accessibility Inspector
Xcode > Open Developer Tool > Accessibility Inspector
Features:
- Shows accessibility hierarchy
- Highlights focus
- Tests with simulated VoiceOver
- Reports accessibility issues
Manual Testing Checklist
- VoiceOver: All content readable and navigable
- Keyboard: All functions accessible without touch
- Voice Control: "Show labels" reveals all elements
- Text Size: App functional at largest setting
- Dark Mode: Works correctly
- Color Contrast: Meets 4.5:1 or 3:1 standard
- Touch Targets: All buttons 44x44 minimum
- Alt Text: All images have meaningful descriptions
- Videos: Captions and descriptions present
- Motion: No auto-playing animations causing distraction
Accessibility Testing Tools
- Apple's Accessibility Inspector (free, built-in)
- WAVE (web accessibility tool, some iOS features)
- Color Contrast Analyzer
- Lighthouse (for web components)
11. App Store Review for Accessibility
Apple's Accessibility Review
Apple specifically checks:
- VoiceOver functionality
- Dynamic Type support
- Color contrast
- Keyboard navigation
- Touch target sizing
- Meaningful alt text for images
Common Rejection Reasons (Accessibility)
"Your app contains images but most lack descriptions.
Users with VoiceOver cannot understand image content."
Fix: Add accessibilityLabel to all meaningful images
"Interactive elements cannot be accessed via keyboard.
Users who cannot use touch cannot use these features."
Fix: Implement full keyboard navigation with focusable()
Accessibility Compliance Checklist
- All interactive elements have accessibility labels
- VoiceOver tested and works fully
- Color contrast meets 4.5:1 standard
- Text scales with Dynamic Type
- Touch targets minimum 44x44 points
- Keyboard navigation complete
- Images have meaningful alt text
- Videos have captions
- Audio descriptions for visual content
- No content trapped (all reachable)
- Motion/animation can be disabled
- Accessibility Inspector shows no warnings
Next Steps
- Enable VoiceOver on device
- Navigate your app with VoiceOver
- Fix any issues found
- Test with full keyboard navigation
- Check color contrast
- Test Dynamic Type at large sizes
- Have person with disability test
- Fix remaining issues
- Submit with confidence
Accessible apps aren't just ethical—they're smarter business.