Accessibility Trait: суть, какие бывают виды и как работают в разработке

Автор: IT Sectr Опубликовано: 2026-05-16 Время чтения: 9 мин

Accessibility Trait — это свойство элемента iOS, которое определяет его роль и поведение для VoiceOver. Трейт сообщает screen reader, как элемент должен быть озвучен и какие жесты доступны: является ли он кнопкой, заголовком, ссылкой или полем поиска. По данным Apple UIAccessibilityTraits, 2024, система поддерживает 15+ констант, которые можно комбинировать побитовой маской. Правильно выбранный трейт экономит до 50% времени навигации для пользователей VoiceOver.

Главное

  • Accessibility Trait — роль элемента iOS для VoiceOver; задаётся через константы UIAccessibilityTraits
  • Трейты можно комбинировать через оператор | для создания сложных ролей (кнопка + выбран)
  • Каждый элемент может иметь несколько трейтов одновременно, но не более 3-4 для избежания путаницы
  • Неправильный трейт (например, StaticText для кнопки) ломает сценарий взаимодействия: пользователь не знает, доступен ли жест
  • В Android аналог — атрибуты role и className в AccessibilityNodeInfo

Что такое Accessibility Trait

Accessibility Trait — флаг, устанавливаемый на элементе UIView для указания его семантической роли VoiceOver. Трейт — один из трёх компонентов accessibility-триады Apple: Label (имя), Hint (описание), Trait (роль). iOS использует битовую маску UIAccessibilityTraits (UInt64), где каждый бит соответствует определённой роли. VoiceOver читает роль после Label и Hint: «Кнопка Отправить. Откроет форму» — «Кнопка» добавлена благодаря трейту UIAccessibilityTraitButton.

По умолчанию UIButton получает UIAccessibilityTraitButton, UILabel — UIAccessibilityTraitStaticText, UIImageView — UIAccessibilityTraitImage. При использовании кастомных контролов разработчик обязан установить трейт вручную. Apple Human Interface Guidelines, 2024, называют это «одним из самых критичных шагов в обеспечении accessibility».

Без правильного трейта пользователь не знает, какой жест применить: одинарное касание (активация кнопки), двойное касание (увеличение) или жест смахивания (переключатель). Трейт решает, какие жесты VoiceOver активирует на элементе.

Техническая реализация UIAccessibilityTraits

UIAccessibilityTraits — это typealias UInt64. Каждый трейт — константа, где установлен ровно один бит. Например, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Комбинация достигается побитовым ИЛИ: 0x0001 | 0x0008 = 0x0009. VoiceOver анализирует маску и определяет поведение.

Основные типы трейтов iOS

iOS предоставляет более 15 констант трейтов. Рассмотрим основные, используемые в 90% сценариев:

ТрейтКонстантаПоведение VoiceOver
ButtonUIAccessibilityTraitButtonАктивация через двойное касание
HeaderUIAccessibilityTraitHeaderБыстрая навигация по заголовкам
LinkUIAccessibilityTraitLinkАктивация как ссылка
StaticTextUIAccessibilityTraitStaticTextТолько чтение, без активации
SearchFieldUIAccessibilityTraitSearchFieldПоле поиска с особым поведением
ImageUIAccessibilityTraitImageИзображение, без жеста активации
SelectedUIAccessibilityTraitSelectedСостояние «выбран»
PlaysSoundUIAccessibilityTraitPlaysSoundВоспроизводит звук при активации
KeyboardKeyUIAccessibilityTraitKeyboardKeyКлавиша клавиатуры
TabBarUIAccessibilityTraitTabBarЭлемент таб-бара

Константы доступны в UIKit с iOS 3.0. В iOS 14+ добавлена поддержка UIAccessibilityTraits в SwiftUI через модификатор .accessibilityAddTraits().

Редкие, но полезные трейты

UIAccessibilityTraitAdjustable — для регулируемых значений (ползунки, пикеры, Volume-слайдеры). VoiceOver позволяет проводить вверх/вниз для изменения значения с шагом, определённым через accessibilityIncrement и accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — для элементов с часто меняющимся значением (таймер, индикатор загрузки). VoiceOver не зачитывает значение при каждом изменении, а берёт паузу. UIAccessibilityTraitAllowsDirectInteraction — для элементов, с которыми пользователь может взаимодействовать напрямую (клавиатура, рисовалка), минуя VoiceOver-жесты.

Комбинирование трейтов

Один элемент может иметь несколько трейтов одновременно — комбинация задаётся через побитовое ИЛИ (|). Пример: кнопка, которая сейчас выбрана — Button | Selected. VoiceOver произнесёт: «Выбрано. Отфильтровано по цене. Кнопка».

Установка трейтов в коде:

swift
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)

// Или через маску:
filterButton.accessibilityTraits = [.button, .selected]

Для кастомных UIView, где трейт не установлен по умолчанию:

swift
class CustomToggle: UIControl {
    override var accessibilityTraits: UIAccessibilityTraits {
        get {
            if isOn {
                return [.button, .selected]
            } else {
                return .button
            }
        }
        set {}
    }
}

Правило комбинирования: не более 3-4 трейтов на элемент. Избыточные трейты (например, Button + Link + Header) делают объявление VoiceOver слишком длинным и запутанным. По данным Apple, «каждое дополнительное свойство увеличивает когнитивную нагрузку на пользователя».

SwiftUI: модификаторы трейтов

В SwiftUI трейты задаются через модификаторы .accessibilityAddTraits() и .accessibilityRemoveTraits(). Пример: Text("Заголовок").font(.largeTitle).accessibilityAddTraits(.isHeader). Модификатор .isHeader добавляет UIAccessibilityTraitHeader. Список трейтов SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.

Типовые ошибки при выборе трейта

StaticText вместо Button — кастомный контрол, визуально выглядящий как кнопка, получает трейт StaticText по умолчанию. VoiceOver не предлагает жест активации, пользователь не может «нажать» элемент. Решение: явно установить .button.

Image без трейта — UIImageView с включённым accessibility получает трейт Image, даже если на самом деле это кнопка для увеличения фото. Назначьте .button и Label «Увеличить фото». По данным WWDC 2023, «Deliver an Exceptional Accessibility Experience», 40% accessibility-регрессий в новых версиях приложений вызваны именно несоответствием трейта.

Header на каждом элементе — трейт Header предназначен для структурных заголовков экрана. Если сделать каждый UILabel заголовком, ротор VoiceOver с режимом «Заголовки» станет бесполезным — он будет останавливаться на каждом слове.

Как исправить: чек-лист

  • Каждый интерактивный кастомный элемент получает трейт Button, Link или Adjustable
  • Заголовки секций получают трейт Header (не StaticText)
  • Изображения-кнопки получают трейт Button + Selected при состоянии selected
  • Элементы без жеста — StaticText или Image (только чтение)

Регрессионные баги при смене UIButton на UIControl

Частая причина потери трейта — рефакторинг: разработчик заменяет UIButton на UIControl для кастомного отображения. UIButton автоматически получает трейт Button, UIControl — нет. После рефакторинга нужно явно задать accessibilityTraits = .button. Добавьте проверку в код-ревью: «Если заменили UIButton на UIControl — проверьте трейт».

Трейты и динамические состояния

Для элементов с изменяющимся состоянием (например, кнопка лайка) трейт должен меняться динамически. В состоянии «не лайкнуто» — Button, в состоянии «лайкнуто» — Button + Selected + Image (если иконка). VoiceOver меняет объявление: «Нравится. Кнопка» vs «Выбрано. Нравится. Кнопка». Используйте accessibilityValue для передачи состояния, если трейт Selected недостаточен. Актуально для кнопок подписки, избранного, фильтров и переключателей.

Android-аналог: role и className

В Android прямой аналогии трейтам нет. Вместо битовой маски используются:

  • className — значение AccessibilityNodeInfo.className (android.widget.Button, android.widget.TextView)
  • role — атрибут в XML (роль определяется типом View)
  • stateDescription — аналог Selected: добавление описания состояния (включено/выключено)

Для кастомных View в Android нужно переопределить onInitializeAccessibilityNodeInfo:

kotlin
class CustomButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    override fun onInitializeAccessibilityNodeInfo(
        info: AccessibilityNodeInfo
    ) {
        super.onInitializeAccessibilityNodeInfo(info)
        info.className = "android.widget.Button"
        info.isClickable = true
    }
}

Разработчикам Flutter нужно использовать параметр semanticsRole в виджете Semantics: button, header, image, link, textField и другие. Дополнительно доступно semanticsLabel и semanticsHint — полный аналог iOS-триады Label + Hint + Trait.

Web-аналоги: WAI-ARIA role

Для веб-версий мобильных приложений (PWA, WebView) используется атрибут role из WAI-ARIA: role="button", role="heading", role="link". Это прямой аналог accessibilityTraits. В гибридных приложениях проверяйте, что WebView передаёт ARIA-роли в нативный accessibility-слой. Для этого используйте протокол UIAccessibilityContainerDataTable в iOS или setAccessibilityDelegate в Android. WebView с включённым JavaScript может не передавать ARIA-роли корректно — тестируйте отдельно.

AccessibilityNodeInfo: добавочные действия

В Android можно добавить кастомные действия в AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK и ACTION_LONG_CLICK. Это аналог трейта Button с дополнительными жестами. Для слайдеров используйте ACTION_SET_PROGRESS — аналог Adjustable. Для Spinner и DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE и ACTION_SET_TIME.

Проверка и тестирование трейтов

Xcode Accessibility Inspector — основной инструмент для iOS: выберите элемент и просмотрите поле Traits. Оно покажет список установленных трейтов. VoiceOver-ротор с режимом «Элементы» позволяет пройтись по всем контролам экрана.

Автоматизированный тест на Swift для проверки трейта:

swift
func testSubmitButtonTrait() {
    let app = XCUIApplication()
    app.launch()
    let submitButton = app.buttons["Отправить"]
    XCTAssertTrue(submitButton.isEnabled)
    // XCUIElement не предощает прямой доступ к трейтам
    // Проверка через активацию жеста
    submitButton.tap()
    XCTAssertTrue(app.staticTexts["Форма отправлена"].exists)
}

Ручная проверка через VoiceOver: включите VoiceOver, проведите пальцем до элемента, коснитесь дважды — элемент должен активироваться, если это Button. Если элемент не реагирует на двойное касание, трейт указан неверно. Используйте жест Rotor для переключения между режимами («Заголовки», «Ссылки», «Кнопки») — каждый режим покажет только элементы с соответствующим трейтом.

Unit-тестирование трейтов в iOS

До iOS 14 unit-тесты не имели прямого доступа к accessibilityTraits. Начиная с iOS 14, свойство доступно: XCTAssertEqual(customButton.accessibilityTraits, .button). Используйте это в модульных тестах для проверки кастомных контролов. Рекомендуется тестировать каждый новый кастомный UIView на корректность трейта, особенно после рефакторинга или смены родительского класса.

Часто задаваемые вопросы

Сколько трейтов можно задать одному элементу?

До 3-4 трейтов на элемент. Большее количество делает объявление VoiceOver избыточным. Используйте комбинации: Button + Selected, Header + StaticText.

Какой трейт у UIButton по умолчанию?

UIAccessibilityTraitButton. iOS автоматически устанавливает его для всех экземпляров UIButton. Если вы наследуетесь от UIView и имитируете кнопку, трейт нужно установить вручную.

Есть ли трейт «Adjustable» и для чего он?

Да, UIAccessibilityTraitAdjustable — для элементов с регулируемым значением (ползунки, пикеры, счётчики). VoiceOver позволяет проводить вверх/вниз для изменения значения и читает текущее состояние.

Как проверить трейты в SwiftUI?

Используйте модификатор .accessibilityAddTraits(): Text("Заголовок").font(.title).accessibilityAddTraits(.isHeader). Метод работает на iOS 14+.

Что будет, если не задать трейт кастомному контролу?

VoiceOver присвоит трейт None. Элемент не получит роль — screen reader прочитает только Label без указания типа. Пользователь не узнает, доступен ли жест активации.

Итоги

  • Accessibility Trait — битовая маска UIAccessibilityTraits, определяющая роль элемента iOS для VoiceOver (Button, Header, Link, StaticText и другие)
  • Трейты комбинируются через побитовое ИЛИ ([] в Swift), не более 3-4 на элемент
  • Кастомные UIView обязаны получать явный трейт — по умолчанию может быть None или Image
  • В Android роль задаётся через className в AccessibilityNodeInfo, в Flutter — через semanticsRole
  • Неправильный трейт (StaticText для кнопки) ломает сценарий VoiceOver: нет жеста активации
  • Проверяйте трейты через Accessibility Inspector в Xcode и VoiceOver-ротор
  • В SwiftUI используйте .accessibilityAddTraits() для настройки трейтов декларативно

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также