Accessibility Trait — este o proprietate a elementului iOS care determină rolul și comportamentul său pentru VoiceOver. Traitul informează cititorul de ecran cum trebuie să fie sonorizat elementul și ce gesturi sunt disponibile: dacă este un buton, titlu, link sau câmp de căutare. Conform Apple UIAccessibilityTraits, 2024, sistemul suportă 15+ constante care pot fi combinate printr-o mască de biți. Un trait ales corect economisește până la 50% din timpul de navigare pentru utilizatorii VoiceOver.
Principalele puncte
Accessibility Trait — un flag setat pe elementul UIView pentru a indica rolul său semantic pentru VoiceOver. Traitul este una dintre cele trei componente ale triadei accessibility Apple: Label (nume), Hint (descriere), Trait (rol). iOS folosește masca de biți UIAccessibilityTraits (UInt64), unde fiecare bit corespunde unui rol specific. VoiceOver citește rolul după Label și Hint: „Buton Trimite. Va deschide formularul” — „Buton” a fost adăugat datorită traitului UIAccessibilityTraitButton.
În mod implicit, UIButton primește UIAccessibilityTraitButton, UILabel — UIAccessibilityTraitStaticText, UIImageView — UIAccessibilityTraitImage. La utilizarea controalelor personalizate, dezvoltatorul este obligat să seteze manual traitul. Apple Human Interface Guidelines, 2024, numesc acest lucru „unul dintre cei mai critici pași în asigurarea accessibility”.
Fără un trait corect, utilizatorul nu știe ce gest să aplice: atingere simplă (activarea butonului), atingere dublă (mărire) sau gest de glisare (comutator). Traitul decide ce gesturi VoiceOver activează pe element.
UIAccessibilityTraits — este un typealias UInt64. Fiecare trait este o constantă în care este setat exact un bit. De exemplu, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Combinația se obține prin SAU bit cu bit: 0x0001 | 0x0008 = 0x0009. VoiceOver analizează masca și determină comportamentul.
iOS oferă peste 15 constante de traituri. Să examinăm principalele, utilizate în 90% din scenarii:
| Trait | Constantă | Comportament VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | Activare prin atingere dublă |
| Header | UIAccessibilityTraitHeader | Navigare rapidă prin titluri |
| Link | UIAccessibilityTraitLink | Activare ca link |
| StaticText | UIAccessibilityTraitStaticText | Doar citire, fără activare |
| SearchField | UIAccessibilityTraitSearchField | Câmp de căutare cu comportament special |
| Image | UIAccessibilityTraitImage | Imagine, fără gest de activare |
| Selected | UIAccessibilityTraitSelected | Starea „selectat” |
| PlaysSound | UIAccessibilityTraitPlaysSound | Redă sunet la activare |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | Tastă de tastatură |
| TabBar | UIAccessibilityTraitTabBar | Element de bară de file |
Constantele sunt disponibile în UIKit din iOS 3.0. În iOS 14+ a fost adăugat suportul pentru UIAccessibilityTraits în SwiftUI prin modificatorul .accessibilityAddTraits().
UIAccessibilityTraitAdjustable — pentru valori reglabile (glisiere, pickere, glisiere de volum). VoiceOver permite glisarea în sus/jos pentru a modifica valoarea cu pasul definit prin accessibilityIncrement și accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — pentru elemente cu valoare care se schimbă frecvent (timer, indicator de încărcare). VoiceOver nu citește valoarea la fiecare schimbare, ci face o pauză. UIAccessibilityTraitAllowsDirectInteraction — pentru elemente cu care utilizatorul poate interacționa direct (tastatură, unealtă de desen), ocolind gesturile VoiceOver.
Un element poate avea mai multe traituri simultan — combinația se specifică prin SAU bit cu bit (|). Exemplu: un buton care este selectat în prezent — Button | Selected. VoiceOver va rosti: „Selectat. Filtrat după preț. Buton”.
Setarea traiturilor în cod:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// Sau prin mască:
filterButton.accessibilityTraits = [.button, .selected]
Pentru UIView-uri personalizate unde traitul nu este setat implicit:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
Regula combinării: nu mai mult de 3-4 traituri pe element. Traiturile în exces (de exemplu, Button + Link + Header) fac declarația VoiceOver prea lungă și confuză. Potrivit Apple, „fiecare proprietate suplimentară crește încărcătura cognitivă a utilizatorului”.
În SwiftUI, traiturile se setează prin modificatorii .accessibilityAddTraits() și .accessibilityRemoveTraits(). Exemplu: Text("Titlu").font(.largeTitle).accessibilityAddTraits(.isHeader). Modificatorul .isHeader adaugă UIAccessibilityTraitHeader. Lista traiturilor SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.
StaticText în loc de Button — un control personalizat care arată vizual ca un buton primește implicit traitul StaticText. VoiceOver nu oferă un gest de activare, utilizatorul nu poate „apăsa” elementul. Soluție: setați explicit .button.
Image fără trait — UIImageView cu accessibility activat primește traitul Image, chiar dacă este de fapt un buton pentru mărirea fotografiei. Atribuiți .button și Label „Mărește fotografia”. Potrivit WWDC 2023, „Deliver an Exceptional Accessibility Experience”, 40% din regresiile de accessibility în versiunile noi ale aplicațiilor sunt cauzate tocmai de nepotrivirea traitului.
Header pe fiecare element — traitul Header este destinat titlurilor structurale ale ecranului. Dacă faceți fiecare UILabel un titlu, rotorul VoiceOver în modul „Titluri” va deveni inutil — se va opri pe fiecare cuvânt.
Cauza frecventă a pierderii traitului — refactorizarea: dezvoltatorul înlocuiește UIButton cu UIControl pentru afișare personalizată. UIButton primește automat traitul Button, UIControl — nu. După refactorizare, trebuie setat explicit accessibilityTraits = .button. Adăugați o verificare în code review: „Dacă ați înlocuit UIButton cu UIControl — verificați traitul”.
Pentru elementele cu stare variabilă (de exemplu, butonul de like), traitul trebuie să se schimbe dinamic. În starea „neapreciat” — Button, în starea „apreciat” — Button + Selected + Image (dacă există pictogramă). VoiceOver schimbă anunțul: „Apreciază. Buton” vs „Selectat. Apreciază. Buton”. Utilizați accessibilityValue pentru transmiterea stării, dacă traitul Selected nu este suficient. Relevant pentru butoanele de abonare, favorite, filtre și comutatoare.
În Android nu există un analog direct al traiturilor. În locul măștii de biți se folosesc:
Pentru View-uri personalizate în Android trebuie suprascrisă 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
}
}
Dezvoltatorii Flutter trebuie să folosească parametrul semanticsRole în widgetul Semantics: button, header, image, link, textField și altele. Suplimentar, sunt disponibile semanticsLabel și semanticsHint — analogul complet al triadei iOS Label + Hint + Trait.
Pentru versiunile web ale aplicațiilor mobile (PWA, WebView) se folosește atributul role din WAI-ARIA: role="button", role="heading", role="link". Acesta este analogul direct al accessibilityTraits. În aplicațiile hibride, verificați dacă WebView transmite rolurile ARIA în stratul nativ de accessibility. Pentru aceasta, utilizați protocolul UIAccessibilityContainerDataTable în iOS sau setAccessibilityDelegate în Android. WebView cu JavaScript activat poate să nu transmită corect rolurile ARIA — testați separat.
În Android se pot adăuga acțiuni personalizate în AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK și ACTION_LONG_CLICK. Acesta este analogul traitului Button cu gesturi suplimentare. Pentru glisiere, utilizați ACTION_SET_PROGRESS — analogul Adjustable. Pentru Spinner și DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE și ACTION_SET_TIME.
Xcode Accessibility Inspector — instrumentul principal pentru iOS: selectați elementul și vizualizați câmpul Traits. Acesta va arăta lista traiturilor setate. Rotorul VoiceOver în modul „Elemente” permite parcurgerea tuturor controalelor ecranului.
Test automatizat în Swift pentru verificarea traitului:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["Trimite"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElement nu oferă acces direct la traituri
// Verificare prin activarea gestului
submitButton.tap()
XCTAssertTrue(app.staticTexts["Formular trimis"].exists)
}
Verificarea manuală prin VoiceOver: activați VoiceOver, deplasați degetul până la element, atingeți de două ori — elementul ar trebui să se activeze, dacă este Button. Dacă elementul nu reacționează la atingerea dublă, traitul este incorect. Utilizați gestul Rotor pentru a comuta între moduri („Titluri”, „Linkuri”, „Butoane”) — fiecare mod va afișa doar elementele cu traitul corespunzător.
Înainte de iOS 14, testele unitare nu aveau acces direct la accessibilityTraits. Începând cu iOS 14, proprietatea este disponibilă: XCTAssertEqual(customButton.accessibilityTraits, .button). Utilizați acest lucru în testele modulare pentru verificarea controalelor personalizate. Se recomandă testarea fiecărui nou UIView personalizat pentru corectitudinea traitului, în special după refactorizare sau schimbarea clasei părinte.
Întrebări frecvente
Până la 3-4 traituri pe element. Un număr mai mare face declarația VoiceOver redundantă. Utilizați combinații: Button + Selected, Header + StaticText.
UIAccessibilityTraitButton. iOS îl setează automat pentru toate instanțele UIButton. Dacă moșteniți de la UIView și simulați un buton, traitul trebuie setat manual.
Da, UIAccessibilityTraitAdjustable — pentru elemente cu valoare reglabilă (glisiere, pickere, contoare). VoiceOver permite glisarea în sus/jos pentru a modifica valoarea și citește starea curentă.
Utilizați modificatorul .accessibilityAddTraits(): Text("Titlu").font(.title).accessibilityAddTraits(.isHeader). Metoda funcționează pe iOS 14+.
VoiceOver va atribui traitul None. Elementul nu va primi un rol — cititorul de ecran va citi doar Label fără a specifica tipul. Utilizatorul nu va ști dacă gestul de activare este disponibil.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și