Accessibility Trait — је својство iOS елемента које одређује његову улогу и понашање за VoiceOver. Траит говори screen reader-у како елемент треба да буде изговорен и који гестови су доступни: да ли је дугме, наслов, линк или поље за претрагу. Према Apple UIAccessibilityTraits, 2024, систем подржава 15+ константи које се могу комбиновати бит масци. Правилно изабран траит штеди до 50% времена навигације за VoiceOver кориснике.
Главно
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 — је typealias UInt64. Сваки траит је константа где је постављен тачно један бит. На пример, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Комбинација се постиже битским ИЛИ: 0x0001 | 0x0008 = 0x0009. VoiceOver анализира маску и одређује понашање.
iOS нуди више од 15 константи траитова. Размотримо основне, који се користе у 90% сценарија:
| Траит | Константа | Понашање VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | Активација двоструким додиром |
| Header | UIAccessibilityTraitHeader | Брза навигација по насловима |
| Link | UIAccessibilityTraitLink | Активација као линк |
| StaticText | UIAccessibilityTraitStaticText | Само читање, без активације |
| SearchField | UIAccessibilityTraitSearchField | Поље за претрагу са посебним понашањем |
| Image | UIAccessibilityTraitImage | Слика, без геста активације |
| Selected | UIAccessibilityTraitSelected | Стање „изабран“ |
| PlaysSound | UIAccessibilityTraitPlaysSound | Репродукује звук при активацији |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | Тастер тастатуре |
| TabBar | UIAccessibilityTraitTabBar | Елемент таб-бара |
Константе су доступне у UIKit од iOS 3.0. У iOS 14+ додата је подршка за UIAccessibilityTraits у SwiftUI кроз модификатор .accessibilityAddTraits().
UIAccessibilityTraitAdjustable — за подесиве вредности (клизачи, бирачи, клизачи јачине звука). VoiceOver омогућава превлачење горе/доле за промену вредности са кораком дефинисаним кроз accessibilityIncrement и accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — за елементе са често променљивом вредношћу (тајмер, индикатор учитавања). VoiceOver не чита вредност при свакој промени, већ прави паузу. UIAccessibilityTraitAllowsDirectInteraction — за елементе са којима корисник може директно да интерагује (тастатура, алат за цртање), без VoiceOver гестова.
Један елемент може имати више траитова истовремено — комбинација се задаје битским ИЛИ (|). Пример: дугме које је тренутно изабрано — Button | Selected. VoiceOver ће изговорити: „Изабрано. Филтрирано по цени. Дугме“.
Постављање траитова у коду:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// Или преко маске:
filterButton.accessibilityTraits = [.button, .selected]
За прилагођене UIView, где траит није подразумевано постављен:
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-ју траитови се постављају кроз модификаторе .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 ротор у режиму „Наслови“ постаће бескористан — заустављаће се на свакој речи.
Чести узрок губитка траита — рефакторинг: програмер замењује UIButton са UIControl ради прилагођеног приказа. UIButton аутоматски добија траит Button, UIControl — не. Након рефакторинга потребно је експлицитно поставити accessibilityTraits = .button. Додајте проверу у код-ревју: „Ако сте заменили UIButton са UIControl — проверите траит“.
За елементе са променљивим стањем (нпр. дугме за лајк) траит треба да се мења динамички. У стању „није лајковано“ — Button, у стању „лајковано“ — Button + Selected + Image (ако постоји икона). VoiceOver мења изјаву: „Свиђа ми се. Дугме“ vs „Изабрано. Свиђа ми се. Дугме“. Користите accessibilityValue за пренос стања, ако траит Selected није довољан. Важи за дугмад за претплату, омиљене, филтере и прекидаче.
У Android-у не постоји директни аналог траитовима. Уместо бит маске користе се:
За прилагођене View у Android-у потребно је прегазити onInitializeAccessibilityNodeInfo:
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.
За веб верзије мобилних апликација (PWA, WebView) користи се атрибут role из WAI-ARIA: role="button", role="heading", role="link". Ово је директни аналог accessibilityTraits. У хибридним апликацијама проверите да WebView преноси ARIA улоге у нативни accessibility слој. За то користите протокол UIAccessibilityContainerDataTable у iOS-у или setAccessibilityDelegate у Android-у. WebView са укљученим JavaScript можда неће исправно преносити ARIA улоге — тестирајте одвојено.
У 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-у за проверу траита:
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 за пребацивање између режима („Наслови“, „Линкови“, „Дугмад“) — сваки режим ће приказати само елементе са одговарајућим траитом.
Пре iOS 14, unit тестови нису имали директан приступ accessibilityTraits. Почевши од iOS 14, својство је доступно: XCTAssertEqual(customButton.accessibilityTraits, .button). Користите ово у модуларним тестовима за проверу прилагођених контрола. Препоручује се тестирање сваког новог прилагођеног UIView на исправност траита, посебно након рефакторинга или промене родитељске класе.
Често постављана питања
До 3-4 траита по елементу. Већи број чини VoiceOver изјаву сувишном. Користите комбинације: Button + Selected, Header + StaticText.
UIAccessibilityTraitButton. iOS га аутоматски поставља за све инстанце UIButton-а. Ако наслеђујете од UIView-а и имитирате дугме, траит се мора ручно поставити.
Да, UIAccessibilityTraitAdjustable — за елементе са подесивом вредношћу (клизачи, бирачи, бројачи). VoiceOver омогућава превлачење горе/доле за промену вредности и чита тренутно стање.
Користите модификатор .accessibilityAddTraits(): Text("Наслов").font(.title).accessibilityAddTraits(.isHeader). Метод ради на iOS 14+.
VoiceOver ће доделити траит None. Елемент неће добити улогу — screen reader ће прочитати само Label без навођења типа. Корисник неће знати да ли је гест активације доступан.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође