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. Траит је један од три компонента Apple accessibility тријаде: 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 — за подесиве вредности (клизачи, бирачи, клизачи јачине звука). 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође