Accessibility Trait è una proprietà di un elemento iOS che ne determina il ruolo e il comportamento per VoiceOver. Il trait indica allo screen reader come l'elemento deve essere annunciato e quali gesti sono disponibili: se si tratta di un pulsante, un'intestazione, un collegamento o un campo di ricerca. Secondo Apple UIAccessibilityTraits, 2024, il sistema supporta oltre 15 costanti che possono essere combinate utilizzando una maschera di bit. Un trait scelto correttamente fa risparmiare fino al 50% del tempo di navigazione per gli utenti di VoiceOver.
Punti Chiave
Accessibility Trait è un flag impostato su un elemento UIView per indicare il suo ruolo semantico a VoiceOver. Il trait è uno dei tre componenti della triade di accessibilità di Apple: Label (nome), Hint (descrizione), Trait (ruolo). iOS utilizza la maschera di bit UIAccessibilityTraits (UInt64), dove ogni bit corrisponde a un ruolo specifico. VoiceOver legge il ruolo dopo Label e Hint: “Pulsante Invia. Aprirà un modulo” — “Pulsante” viene aggiunto grazie al trait UIAccessibilityTraitButton.
Per impostazione predefinita, UIButton riceve UIAccessibilityTraitButton, UILabel riceve UIAccessibilityTraitStaticText, UIImageView riceve UIAccessibilityTraitImage. Quando si utilizzano controlli personalizzati, lo sviluppatore deve impostare il trait manualmente. Apple Human Interface Guidelines, 2024, definiscono questo “uno dei passaggi più critici per garantire l'accessibilità”.
Senza il trait corretto, l'utente non sa quale gesto applicare: tocco singolo (attivazione pulsante), doppio tocco (zoom) o gesto di scorrimento (interruttore). Il trait determina quali gesti di VoiceOver vengono attivati sull'elemento.
UIAccessibilityTraits è un typealias UInt64. Ogni trait è una costante con esattamente un bit impostato. Ad esempio, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Le combinazioni si ottengono con OR bit a bit: 0x0001 | 0x0008 = 0x0009. VoiceOver analizza la maschera e determina il comportamento.
iOS fornisce oltre 15 costanti di trait. Vediamo i principali utilizzati nel 90% degli scenari:
| Trait | Costante | Comportamento VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | Attivazione con doppio tocco |
| Header | UIAccessibilityTraitHeader | Navigazione rapida per intestazioni |
| Link | UIAccessibilityTraitLink | Attivazione come collegamento |
| StaticText | UIAccessibilityTraitStaticText | Sola lettura, nessuna attivazione |
| SearchField | UIAccessibilityTraitSearchField | Campo di ricerca con comportamento speciale |
| Image | UIAccessibilityTraitImage | Immagine, nessun gesto di attivazione |
| Selected | UIAccessibilityTraitSelected | Stato “selezionato” |
| PlaysSound | UIAccessibilityTraitPlaysSound | Riproduce un suono all'attivazione |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | Tasto della tastiera |
| TabBar | UIAccessibilityTraitTabBar | Elemento della barra delle schede |
Le costanti sono disponibili in UIKit da iOS 3.0. iOS 14+ ha aggiunto il supporto di UIAccessibilityTraits in SwiftUI tramite il modificatore .accessibilityAddTraits().
UIAccessibilityTraitAdjustable — per valori regolabili (slider, selettori, controlli del volume). VoiceOver consente di scorrere verso l'alto/basso per modificare il valore con un passo definito tramite accessibilityIncrement e accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — per elementi con valori che cambiano frequentemente (timer, indicatore di avanzamento). VoiceOver non legge il valore a ogni modifica ma fa una pausa. UIAccessibilityTraitAllowsDirectInteraction — per elementi con cui l'utente può interagire direttamente (tastiera, disegno), bypassando i gesti di VoiceOver.
Un singolo elemento può avere più trait simultaneamente — la combinazione viene impostata utilizzando OR bit a bit (|). Esempio: un pulsante attualmente selezionato — Button | Selected. VoiceOver annuncerà: “Selezionato. Filtrato per prezzo. Pulsante.”
Impostazione dei trait nel codice:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// Oppure tramite maschera:
filterButton.accessibilityTraits = [.button, .selected]
Per UIView personalizzato dove il trait non è impostato per impostazione predefinita:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
Regola di combinazione: non più di 3-4 trait per elemento. Trait eccessivi (ad esempio, Button + Link + Header) rendono l'annuncio di VoiceOver troppo lungo e confuso. Secondo Apple, “ogni proprietà aggiuntiva aumenta il carico cognitivo dell'utente”.
In SwiftUI, i trait vengono impostati utilizzando i modificatori .accessibilityAddTraits() e .accessibilityRemoveTraits(). Esempio: Text(“Titolo”).font(.largeTitle).accessibilityAddTraits(.isHeader). Il modificatore .isHeader aggiunge UIAccessibilityTraitHeader. Elenco trait SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.
StaticText invece di Button — un controllo personalizzato che visivamente sembra un pulsante riceve il trait StaticText per impostazione predefinita. VoiceOver non offre un gesto di attivazione, quindi l'utente non può “premere” l'elemento. Soluzione: impostare esplicitamente .button.
Immagine senza trait — UIImageView con accessibilità abilitata riceve il trait Image, anche se in realtà è un pulsante per ingrandire una foto. Assegna .button e Label “Ingrandisci foto.” Secondo WWDC 2023, “Deliver an Exceptional Accessibility Experience”, il 40% delle regressioni di accessibilità nelle nuove versioni delle app è causato proprio dalla mancata corrispondenza del trait.
Header su ogni elemento — il trait Header è destinato alle intestazioni strutturali dello schermo. Se ogni UILabel viene reso un'intestazione, il rotore di VoiceOver in modalità “Intestazioni” diventa inutile — si fermerà su ogni parola.
Una causa comune di perdita del trait è il refactoring: uno sviluppatore sostituisce UIButton con UIControl per una visualizzazione personalizzata. UIButton riceve automaticamente il trait Button, UIControl no. Dopo il refactoring, è necessario impostare esplicitamente accessibilityTraits = .button. Aggiungi un controllo nella revisione del codice: “Se hai sostituito UIButton con UIControl — verifica il trait.”
Per elementi con stato mutevole (ad esempio, un pulsante “Mi piace”), il trait dovrebbe cambiare dinamicamente. Nello stato “non mi piace” — Button, nello stato “mi piace” — Button + Selected + Image (se c'è un'icona). VoiceOver cambia l'annuncio: “Mi piace. Pulsante.” vs “Selezionato. Mi piace. Pulsante.” Usa accessibilityValue per comunicare lo stato se il trait Selected è insufficiente. Rilevante per pulsanti di iscrizione, preferiti, filtri e interruttori.
Su Android, non esiste un equivalente diretto dei trait. Invece di una maschera di bit, vengono utilizzati:
Per le View personalizzate su Android, è necessario sovrascrivere 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
}
}
Gli sviluppatori Flutter dovrebbero usare il parametro semanticsRole nel widget Semantics: button, header, image, link, textField e altri. Inoltre, semanticsLabel e semanticsHint sono disponibili — un equivalente completo della triade iOS Label + Hint + Trait.
Per le versioni web delle app mobili (PWA, WebView), viene utilizzato l'attributo role di WAI-ARIA: role="button", role="heading", role="link". Questo è un equivalente diretto di accessibilityTraits. Nelle app ibride, verifica che WebView passi i ruoli ARIA al layer di accessibilità nativo. Per farlo, usa il protocollo UIAccessibilityContainerDataTable su iOS o setAccessibilityDelegate su Android. Una WebView con JavaScript abilitato potrebbe non passare correttamente i ruoli ARIA — testa separatamente.
Su Android, puoi aggiungere azioni personalizzate a AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK e ACTION_LONG_CLICK. Questo equivale al trait Button con gesti aggiuntivi. Per gli slider, usa ACTION_SET_PROGRESS — equivalente di Adjustable. Per Spinner e DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE e ACTION_SET_TIME.
Xcode Accessibility Inspector è lo strumento principale per iOS: seleziona un elemento e visualizza il campo Traits. Mostrerà l'elenco dei trait impostati. Il rotore di VoiceOver con la modalità “Elementi” consente di navigare attraverso tutti i controlli dello schermo.
Test automatizzato in Swift per verificare un trait:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["Invia"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElement non fornisce accesso diretto ai trait
// Verifica tramite attivazione del gesto
submitButton.tap()
XCTAssertTrue(app.staticTexts["Modulo inviato"].exists)
}
Verifica manuale tramite VoiceOver: attiva VoiceOver, scorri fino all'elemento, tocca due volte — l'elemento dovrebbe attivarsi se è un Button. Se l'elemento non risponde al doppio tocco, il trait è errato. Usa il gesto Rotor per passare da una modalità all'altra (“Intestazioni”, “Collegamenti”, “Pulsanti”) — ogni modalità mostrerà solo gli elementi con il trait corrispondente.
Prima di iOS 14, i test unitari non avevano accesso diretto a accessibilityTraits. A partire da iOS 14, la proprietà è disponibile: XCTAssertEqual(customButton.accessibilityTraits, .button). Usalo nei test unitari per verificare i controlli personalizzati. Si consiglia di testare ogni nuovo UIView personalizzato per la correttezza del trait, specialmente dopo refactoring o cambiamenti della classe genitore.
Domande Frequenti
Fino a 3-4 trait per elemento. Un numero maggiore rende l'annuncio di VoiceOver ridondante. Usa combinazioni: Button + Selected, Header + StaticText.
UIAccessibilityTraitButton. iOS lo imposta automaticamente per tutte le istanze di UIButton. Se erediti da UIView e simuli un pulsante, il trait deve essere impostato manualmente.
Sì, UIAccessibilityTraitAdjustable — per elementi con valori regolabili (slider, selettori, contatori). VoiceOver consente di scorrere verso l'alto/basso per modificare il valore e leggere lo stato corrente.
Usa il modificatore .accessibilityAddTraits(): Text(“Titolo”).font(.title).accessibilityAddTraits(.isHeader). Il metodo funziona su iOS 14+.
VoiceOver assegnerà il trait None. L'elemento non avrà un ruolo — lo screen reader leggerà solo la Label senza indicare il tipo. L'utente non saprà se un gesto di attivazione è disponibile.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche