Реализация поддержки VoiceOver для iOS-приложения
VoiceOver — screen reader, встроенный в iOS. Пользователь взаимодействует с приложением через свайпы и касания, а система зачитывает содержимое экрана. Если ваше приложение не поддерживает VoiceOver, незрячий пользователь не может им пользоваться совсем — не «неудобно», а именно «не может».
App Store ревью не проверяет доступность автоматически, но приложения для государственного сектора, медицины и образования часто требуют соответствия WCAG 2.1 / Section 508 по контракту.
Что конкретно ломается без проработки
Кастомные UIView и SwiftUI View
Стандартные UIButton, UILabel, UITextField — VoiceOver понимает из коробки. Кастомные UIView с нарисованным на CALayer контентом — нет. VoiceOver видит весь кастомный view как один элемент без описания.
Для UIKit: isAccessibilityElement = true, accessibilityLabel, accessibilityHint, accessibilityTraits. accessibilityLabel — что это такое («Кнопка добавить в корзину»). accessibilityHint — что произойдёт при активации («Добавляет товар в корзину, переходит к оформлению»). accessibilityTraits — тип элемента (.button, .link, .image, .header, .selected).
Для SwiftUI: модификаторы .accessibilityLabel(), .accessibilityHint(), .accessibilityAddTraits(). Если несколько вложенных View нужно объединить в один accessible элемент — .accessibilityElement(children: .combine) или .accessibilityElement(children: .ignore) + явный label.
Изображения без alt-текста
UIImageView с isAccessibilityElement = false (по умолчанию) — VoiceOver пропускает. Декоративные изображения — правильно. Смысловые — нет, нужен accessibilityLabel. Иконки в кнопках: если UIButton содержит только UIImageView без текста — нужен accessibilityLabel на кнопке, иначе VoiceOver зачитает имя файла или молчит.
Порядок фокуса
VoiceOver обходит элементы по accessibilityActivationPoint — обычно центр frame. Для сложных layout (overlay, absolute positioning в SwiftUI, кастомные контейнеры) порядок может быть хаотичным. Исправляется через accessibilityElements на родительском контейнере — массив в нужном порядке:
override var accessibilityElements: [Any]? {
get { [titleLabel, priceLabel, addButton] }
set { }
}
В SwiftUI — .accessibilitySortPriority() для управления порядком.
Модальные экраны и кастомные оверлеи
UIAlertController — VoiceOver фокусируется автоматически. Кастомный UIView-оверлей поверх контента — нет. Нужно UIAccessibility.post(notification: .screenChanged, argument: firstElement) чтобы перевести фокус на первый элемент оверлея. При закрытии — post(notification: .screenChanged, argument: triggerButton) чтобы вернуть фокус на кнопку, которая открыла оверлей.
accessibilityViewIsModal = true на контейнере оверлея — скрывает фоновой контент от VoiceOver. Без этого пользователь свайпом может «провалиться» сквозь оверлей на задний контент.
Как мы проводим аудит и внедрение
Включаем VoiceOver (Cmd+F5 в Simulator или тройной клик Home/Side Button на устройстве) и проходим все ключевые флоу: онбординг, главный экран, основные actions (оформить заказ, отправить сообщение, воспроизвести медиа).
Фиксируем: элементы без label, неправильный порядок фокуса, недостижимые элементы, модальные оверлеи без управления фокусом.
Инструменты: Accessibility Inspector (Xcode) — проверяет контрасность, находит элементы без label без запуска приложения. XCTest с XCUIAccessibilityAudit (iOS 17+) — автоматизированный аудит в UI-тестах.
Правки расставляем по приоритету: сначала навигация и ключевые CTA, потом списки и формы, потом медиа-контент.
Срок: 3-5 дней для приложения среднего масштаба. Стоимость зависит от количества экранов и доли кастомных компонентов.







