Accessibility в Swift: делаем iOS-приложения доступными для всех

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Accessibility — это не просто галочка, а суперсила вашего приложения

Представьте: вы создали крутое приложение. Красивый интерфейс, молниеносная анимация, идеальный код на Swift. Вы гордитесь им. Но вдруг оказывается, что 15% пользователей не могут им пользоваться. Для них кнопки слишком мелкие, текст сливается с фоном, а VoiceOver «читает» кашу вместо меню. Знакомо?

Accessibility (доступность) — это не про «обязаловку» и не про «сделаем для галочки». Это про инклюзивность, про заботу и, что немаловажно, про бизнес. В iOS есть мощнейший фреймворк, который позволяет адаптировать приложение под нужды людей с нарушениями зрения, слуха, моторики и когнитивными особенностями. И Swift даёт нам все инструменты, чтобы сделать это элегантно.

В этой статье мы не будем читать скучные лекции. Мы разберём конкретные примеры кода, которые можно сразу внедрить в проект. Поехали!

VoiceOver: как «научить» приложение рассказывать о себе

VoiceOver — это экранный диктор, встроенный в iOS. Он «озвучивает» всё, что происходит на экране: текст, кнопки, жесты. Но если разработчик не подготовил интерфейс, VoiceOver может превратиться в хаос. Давайте исправим это.

Базовая настройка: accessibilityLabel и accessibilityTraits

Самый простой способ помочь VoiceOver — задать метки и типы элементов. Например, у вас есть кнопка с иконкой сердца. Без метки VoiceOver скажет просто «кнопка», а с меткой — «Добавить в избранное».

let favoriteButton = UIButton(type: .system)
favoriteButton.setImage(UIImage(systemName: "heart"), for: .normal)
favoriteButton.accessibilityLabel = "Добавить в избранное"
favoriteButton.accessibilityTraits = .button

Обратите внимание на accessibilityTraits. Это подсказка для VoiceOver, как взаимодействовать с элементом. Для кнопки — .button, для изображения — .image, для заголовка — .header. Используйте .selected, .notEnabled и другие, чтобы точно передать состояние.

Группировка элементов: как не дать VoiceOver «заболтать» пользователя

Иногда логично объединить несколько элементов в один смысловой блок. Например, карточка товара: изображение, название, цена. Если VoiceOver будет читать их по отдельности, пользователь устанет. Решение — UIAccessibilityElement или shouldGroupAccessibilityChildren.

cardView.shouldGroupAccessibilityChildren = true
cardView.accessibilityLabel = "Кроссовки Nike Air, 12 990 рублей"
cardView.accessibilityTraits = .button

Теперь VoiceOver прочитает всю карточку как один элемент. Кратко, ёмко, понятно.

Кастомные действия: когда нужно больше, чем просто «тап»

Бывают сложные элементы: слайдеры, переключатели, список смахиваний. Для них важно добавить кастомные действия. Например, для ячейки с функцией «Удалить» и «Редактировать»:

let deleteAction = UIAccessibilityCustomAction(
    name: "Удалить",
    target: self,
    selector: #selector(deleteItem)
)
let editAction = UIAccessibilityCustomAction(
    name: "Редактировать",
    target: self,
    selector: #selector(editItem)
)
cell.accessibilityCustomActions = [deleteAction, editAction]

Пользователь VoiceOver смахивает вниз или вверх, чтобы выбрать действие. Это значительно улучшает опыт.

Dynamic Type: текст, который подстраивается под каждого

Люди с ослабленным зрением часто увеличивают шрифт в настройках iOS. Если ваше приложение не поддерживает Dynamic Type, текст обрежется, наложится друг на друга или просто станет нечитаемым. Как этого избежать?

Используем динамические стили шрифтов

Вместо жёсткого UIFont.systemFont(ofSize: 16) используйте UIFont.preferredFont(forTextStyle: .body).

label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true

adjustsFontForContentSizeCategory — волшебное свойство, которое заставляет шрифт автоматически реагировать на изменения размера текста в системе. Не забудьте включить его для всех UILabel, UITextField и UITextView.

Работа с Auto Layout и масштабированием

Когда пользователь увеличивает шрифт, элементы интерфейса должны растягиваться, а не ломаться. Используйте UIStackView и относительные констрейнты. Избегайте фиксированной высоты для кнопок и лейблов.

let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 8
stackView.distribution = .fill

let titleLabel = UILabel()
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.adjustsFontForContentSizeCategory = true
titleLabel.numberOfLines = 0 // Разрешаем перенос строк

stackView.addArrangedSubview(titleLabel)

Обратите внимание на numberOfLines = 0. Это разрешает тексту занимать столько строк, сколько нужно. Без этого при большом шрифте текст обрежется.

Управление фокусом: как не потерять пользователя в навигации

VoiceOver перемещается по элементам в порядке, определённом accessibilityElements. По умолчанию это порядок добавления в иерархию. Но что, если нужно изменить логику?

Ручная настройка порядка фокуса

view.accessibilityElements = [firstElement, secondElement, thirdElement]

Это особенно полезно в сложных экранах: формах, модальных окнах, кастомных таблицах. Не заставляйте пользователя тыкать вслепую — проложите чёткий маршрут.

Программный перевод фокуса

Бывают ситуации, когда нужно перевести фокус на конкретный элемент. Например, после отправки формы — на сообщение об ошибке.

UIAccessibility.post(notification: .screenChanged, argument: errorLabel)

Но используйте эту возможность аккуратно. Частые «перескоки» фокуса дезориентируют пользователя. Лучше один раз чётко направить, чем дёргать каждую секунду.

Цветовой контраст и «умные» жесты: детали, которые меняют всё

Accessibility — это не только VoiceOver. Это ещё и визуальная доступность.

Контрастность текста и иконок

Apple рекомендует коэффициент контрастности не менее 4.5:1 для обычного текста и 3:1 для крупного. Проверить это можно в Xcode через Accessibility Inspector. Не надейтесь на глаз — используйте инструменты.

Также учитывайте UIAccessibility.isReduceTransparencyEnabled и UIAccessibility.isReduceMotionEnabled. Если пользователь включил уменьшение прозрачности — убирайте размытые фоны. Если уменьшение движения — отключайте параллакс и анима

Блоги

Рекомендаций по книгам