Implementing VoiceOver Support for iOS Applications
VoiceOver is a screen reader built into iOS. Users interact with the application through swipes and taps, while the system reads the screen content aloud. If your application doesn't support VoiceOver, a visually impaired user cannot use it at all — not "inconvenient", but literally "cannot".
App Store review doesn't check accessibility automatically, but applications for government, healthcare, and education sectors often require WCAG 2.1 / Section 508 compliance by contract.
What Breaks Without Proper Implementation
Custom UIView and SwiftUI View
Standard UIButton, UILabel, UITextField — VoiceOver understands them out of the box. Custom UIView with content drawn on CALayer — no. VoiceOver sees the entire custom view as one element without description.
For UIKit: isAccessibilityElement = true, accessibilityLabel, accessibilityHint, accessibilityTraits. accessibilityLabel — what this is ("Add to Cart button"). accessibilityHint — what happens on activation ("Adds item to cart, proceeds to checkout"). accessibilityTraits — element type (.button, .link, .image, .header, .selected).
For SwiftUI: modifiers .accessibilityLabel(), .accessibilityHint(), .accessibilityAddTraits(). If multiple nested Views need to be combined into one accessible element — .accessibilityElement(children: .combine) or .accessibilityElement(children: .ignore) + explicit label.
Images Without Alt Text
UIImageView with isAccessibilityElement = false (default) — VoiceOver skips it. Decorative images — correct. Meaningful ones — no, needs accessibilityLabel. Icons in buttons: if UIButton contains only UIImageView without text — needs accessibilityLabel on the button, otherwise VoiceOver reads the filename or stays silent.
Focus Order
VoiceOver traverses elements by accessibilityActivationPoint — usually the frame center. For complex layouts (overlays, absolute positioning in SwiftUI, custom containers), order can be chaotic. Fixed via accessibilityElements on parent container — array in correct order:
override var accessibilityElements: [Any]? {
get { [titleLabel, priceLabel, addButton] }
set { }
}
In SwiftUI — .accessibilitySortPriority() to manage order.
Modal Screens and Custom Overlays
UIAlertController — VoiceOver focuses automatically. Custom UIView overlay on top of content — no. Need UIAccessibility.post(notification: .screenChanged, argument: firstElement) to move focus to the first element of overlay. On closing — post(notification: .screenChanged, argument: triggerButton) to return focus to the button that opened the overlay.
accessibilityViewIsModal = true on overlay container — hides background content from VoiceOver. Without this, user can swipe "through" the overlay to background content.
How We Conduct Audit and Implementation
We enable VoiceOver (Cmd+F5 in Simulator or triple-click Home/Side Button on device) and go through all key flows: onboarding, main screen, primary actions (checkout, send message, play media).
We identify: elements without labels, incorrect focus order, unreachable elements, modal overlays without focus management.
Tools: Accessibility Inspector (Xcode) — checks contrast, finds elements without labels without running the app. XCTest with XCUIAccessibilityAudit (iOS 17+) — automated audit in UI tests.
We prioritize fixes: navigation and key CTAs first, then lists and forms, then media content.
Timeframe: 3-5 days for medium-scale application. Cost depends on number of screens and proportion of custom components.







