Accessibility Trait é uma propriedade de elemento iOS que determina sua função e comportamento para o VoiceOver. O trait informa ao screen reader como o elemento deve ser anunciado e quais gestos estão disponíveis: se é um botão, cabeçalho, link ou campo de busca. De acordo com Apple UIAccessibilityTraits, 2024, o sistema suporta mais de 15 constantes que podem ser combinadas usando uma máscara de bits. Um trait escolhido corretamente economiza até 50% do tempo de navegação para usuários do VoiceOver.
Pontos-Chave
Accessibility Trait é uma flag definida em um elemento UIView para indicar sua função semântica ao VoiceOver. O trait é um dos três componentes da tríade de acessibilidade da Apple: Label (nome), Hint (descrição), Trait (função). O iOS usa a máscara de bits UIAccessibilityTraits (UInt64), onde cada bit corresponde a uma função específica. O VoiceOver lê a função após Label e Hint: “Botão Enviar. Abrirá um formulário” — “Botão” é adicionado graças ao trait UIAccessibilityTraitButton.
Por padrão, UIButton recebe UIAccessibilityTraitButton, UILabel recebe UIAccessibilityTraitStaticText, UIImageView recebe UIAccessibilityTraitImage. Ao usar controles personalizados, o desenvolvedor deve definir o trait manualmente. Apple Human Interface Guidelines, 2024, chamam isso de “um dos passos mais críticos para garantir a acessibilidade”.
Sem o trait correto, o usuário não sabe qual gesto aplicar: toque simples (ativação de botão), toque duplo (zoom) ou gesto de deslizar (alternar). O trait determina quais gestos do VoiceOver são ativados no elemento.
UIAccessibilityTraits é um typealias UInt64. Cada trait é uma constante com exatamente um bit definido. Por exemplo, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Combinações são alcançadas com OR bit a bit: 0x0001 | 0x0008 = 0x0009. O VoiceOver analisa a máscara e determina o comportamento.
O iOS fornece mais de 15 constantes de traits. Vamos ver os principais usados em 90% dos cenários:
| Trait | Constante | Comportamento do VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | Ativação por toque duplo |
| Header | UIAccessibilityTraitHeader | Navegação rápida por cabeçalhos |
| Link | UIAccessibilityTraitLink | Ativação como link |
| StaticText | UIAccessibilityTraitStaticText | Somente leitura, sem ativação |
| SearchField | UIAccessibilityTraitSearchField | Campo de busca com comportamento especial |
| Image | UIAccessibilityTraitImage | Imagem, sem gesto de ativação |
| Selected | UIAccessibilityTraitSelected | Estado “selecionado” |
| PlaysSound | UIAccessibilityTraitPlaysSound | Reproduz som ao ativar |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | Tecla de teclado |
| TabBar | UIAccessibilityTraitTabBar | Elemento de barra de abas |
Constantes estão disponíveis em UIKit desde iOS 3.0. iOS 14+ adicionou suporte a UIAccessibilityTraits no SwiftUI através do modificador .accessibilityAddTraits().
UIAccessibilityTraitAdjustable — para valores ajustáveis (sliders, seletores, controles de volume). O VoiceOver permite deslizar para cima/baixo para alterar o valor com um passo definido via accessibilityIncrement e accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — para elementos com valores que mudam frequentemente (temporizador, indicador de progresso). O VoiceOver não lê o valor a cada mudança, mas faz uma pausa. UIAccessibilityTraitAllowsDirectInteraction — para elementos com os quais o usuário pode interagir diretamente (teclado, desenho), ignorando gestos do VoiceOver.
Um único elemento pode ter vários traits simultaneamente — a combinação é definida usando OR bit a bit (|). Exemplo: um botão atualmente selecionado — Button | Selected. O VoiceOver anunciará: “Selecionado. Filtrado por preço. Botão.”
Definindo traits em código:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// Ou através de uma máscara:
filterButton.accessibilityTraits = [.button, .selected]
Para UIView personalizado onde o trait não é definido por padrão:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
Regra de combinação: não mais que 3-4 traits por elemento. Traits excessivos (por exemplo, Button + Link + Header) tornam o anúncio do VoiceOver muito longo e confuso. Segundo a Apple, “cada propriedade adicional aumenta a carga cognitiva do usuário”.
No SwiftUI, os traits são definidos usando os modificadores .accessibilityAddTraits() e .accessibilityRemoveTraits(). Exemplo: Text(“Título”).font(.largeTitle).accessibilityAddTraits(.isHeader). O modificador .isHeader adiciona UIAccessibilityTraitHeader. Lista de traits do SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.
StaticText em vez de Button — um controle personalizado que visualmente parece um botão recebe o trait StaticText por padrão. O VoiceOver não oferece um gesto de ativação, então o usuário não pode “pressionar” o elemento. Solução: definir explicitamente .button.
Imagem sem trait — UIImageView com acessibilidade habilitada recebe o trait Image, mesmo que seja na verdade um botão para ampliar uma foto. Atribua .button e Label “Ampliar foto.” De acordo com WWDC 2023, “Deliver an Exceptional Accessibility Experience”, 40% das regressões de acessibilidade em novas versões de aplicativos são causadas precisamente pela incompatibilidade de trait.
Header em cada elemento — o trait Header é destinado a cabeçalhos estruturais da tela. Se cada UILabel for transformado em cabeçalho, o rotor do VoiceOver no modo “Cabeçalhos” se torna inútil — ele parará em cada palavra.
Uma causa comum de perda de trait é a refatoração: um desenvolvedor substitui UIButton por UIControl para exibição personalizada. UIButton automaticamente recebe o trait Button, UIControl não. Após a refatoração, você precisa definir explicitamente accessibilityTraits = .button. Adicione uma verificação na revisão de código: “Se substituiu UIButton por UIControl — verifique o trait.”
Para elementos com estado mutável (por exemplo, um botão de curtir), o trait deve mudar dinamicamente. No estado “ não curtido” — Button, no estado “curtido” — Button + Selected + Image (se houver um ícone). O VoiceOver muda o anúncio: “Curtir. Botão.” vs “Selecionado. Curtir. Botão.” Use accessibilityValue para transmitir o estado se o trait Selected for insuficiente. Relevante para botões de inscrição, favoritos, filtros e interruptores.
No Android, não há equivalente direto aos traits. Em vez de uma máscara de bits, são usados:
Para Views personalizados no Android, você precisa sobrescrever 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
}
}
Desenvolvedores Flutter devem usar o parâmetro semanticsRole no widget Semantics: button, header, image, link, textField e outros. Além disso, semanticsLabel e semanticsHint estão disponíveis — um equivalente completo da tríade iOS Label + Hint + Trait.
Para versões web de aplicativos móveis (PWA, WebView), o atributo role do WAI-ARIA é usado: role="button", role="heading", role="link". Este é um equivalente direto de accessibilityTraits. Em aplicativos híbridos, verifique se o WebView passa as funções ARIA para a camada de acessibilidade nativa. Para isso, use o protocolo UIAccessibilityContainerDataTable no iOS ou setAccessibilityDelegate no Android. Um WebView com JavaScript habilitado pode não passar as funções ARIA corretamente — teste separadamente.
No Android, você pode adicionar ações personalizadas ao AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK e ACTION_LONG_CLICK. Isso é equivalente ao trait Button com gestos adicionais. Para sliders, use ACTION_SET_PROGRESS — equivalente ao Adjustable. Para Spinner e DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE e ACTION_SET_TIME.
Xcode Accessibility Inspector é a ferramenta principal para iOS: selecione um elemento e veja o campo Traits. Ele mostrará a lista de traits definidos. O rotor do VoiceOver com o modo “Elementos” permite navegar por todos os controles da tela.
Teste automatizado em Swift para verificar um trait:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["Enviar"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElement não fornece acesso direto aos traits
// Verificação através da ativação de gesto
submitButton.tap()
XCTAssertTrue(app.staticTexts["Formulário enviado"].exists)
}
Verificação manual via VoiceOver: ative o VoiceOver, deslize até o elemento, toque duas vezes — o elemento deve ativar se for um Button. Se o elemento não responder ao toque duplo, o trait está incorreto. Use o gesto Rotor para alternar entre modos (“Cabeçalhos”, “Links”, “Botões”) — cada modo mostrará apenas elementos com o trait correspondente.
Antes do iOS 14, os testes unitários não tinham acesso direto a accessibilityTraits. A partir do iOS 14, a propriedade está disponível: XCTAssertEqual(customButton.accessibilityTraits, .button). Use isso em testes unitários para verificar controles personalizados. Recomenda-se testar cada novo UIView personalizado quanto à correção do trait, especialmente após refatoração ou mudança de classe pai.
Perguntas Frequentes
Até 3-4 traits por elemento. Um número maior torna o anúncio do VoiceOver redundante. Use combinações: Button + Selected, Header + StaticText.
UIAccessibilityTraitButton. O iOS define automaticamente para todas as instâncias de UIButton. Se você herdar de UIView e simular um botão, o trait deve ser definido manualmente.
Sim, UIAccessibilityTraitAdjustable — para elementos com valores ajustáveis (sliders, seletores, contadores). O VoiceOver permite deslizar para cima/baixo para alterar o valor e ler o estado atual.
Use o modificador .accessibilityAddTraits(): Text(“Título”).font(.title).accessibilityAddTraits(.isHeader). O método funciona no iOS 14+.
O VoiceOver atribuirá o trait None. O elemento não terá uma função — o screen reader lerá apenas o Label sem indicar o tipo. O usuário não saberá se um gesto de ativação está disponível.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também