Accessibility Trait es una propiedad del elemento iOS que determina su rol y comportamiento para VoiceOver. El trait indica al screen reader cómo debe ser anunciado el elemento y qué gestos están disponibles: si es un botón, encabezado, enlace o campo de búsqueda. Según Apple UIAccessibilityTraits, 2024, el sistema admite más de 15 constantes que se pueden combinar mediante una máscara de bits. Un trait correctamente elegido ahorra hasta un 50% del tiempo de navegación para los usuarios de VoiceOver.
Puntos Clave
Accessibility Trait es una bandera que se establece en un elemento UIView para indicar su rol semántico a VoiceOver. El trait es uno de los tres componentes de la tríada de accesibilidad de Apple: Label (nombre), Hint (descripción), Trait (rol). iOS utiliza la máscara de bits UIAccessibilityTraits (UInt64), donde cada bit corresponde a un rol específico. VoiceOver lee el rol después de Label y Hint: “Botón Enviar. Abrirá un formulario” — “Botón” se añade gracias al trait UIAccessibilityTraitButton.
Por defecto, UIButton obtiene UIAccessibilityTraitButton, UILabel obtiene UIAccessibilityTraitStaticText, UIImageView obtiene UIAccessibilityTraitImage. Al usar controles personalizados, el desarrollador debe establecer el trait manualmente. Apple Human Interface Guidelines, 2024, lo denominan “uno de los pasos más críticos para garantizar la accesibilidad”.
Sin el trait correcto, el usuario no sabe qué gesto aplicar: toque simple (activación de botón), doble toque (ampliación) o gesto de deslizar (conmutador). El trait determina qué gestos de VoiceOver se activan en el elemento.
UIAccessibilityTraits es un typealias UInt64. Cada trait es una constante con exactamente un bit establecido. Por ejemplo, UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Las combinaciones se logran con OR bit a bit: 0x0001 | 0x0008 = 0x0009. VoiceOver analiza la máscara y determina el comportamiento.
iOS proporciona más de 15 constantes de traits. Veamos las principales que se utilizan en el 90% de los casos:
| Trait | Constante | Comportamiento de VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | Activación mediante doble toque |
| Header | UIAccessibilityTraitHeader | Navegación rápida por encabezados |
| Link | UIAccessibilityTraitLink | Activación como enlace |
| StaticText | UIAccessibilityTraitStaticText | Solo lectura, sin activación |
| SearchField | UIAccessibilityTraitSearchField | Campo de búsqueda con comportamiento especial |
| Image | UIAccessibilityTraitImage | Imagen, sin gesto de activación |
| Selected | UIAccessibilityTraitSelected | Estado “seleccionado” |
| PlaysSound | UIAccessibilityTraitPlaysSound | Reproduce sonido al activarse |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | Tecla de teclado |
| TabBar | UIAccessibilityTraitTabBar | Elemento de barra de pestañas |
Las constantes están disponibles en UIKit desde iOS 3.0. iOS 14+ añadió soporte de UIAccessibilityTraits en SwiftUI mediante el modificador .accessibilityAddTraits().
UIAccessibilityTraitAdjustable — para valores ajustables (deslizadores, selectores, controles de volumen). VoiceOver permite deslizar hacia arriba/abajo para cambiar el valor con un paso definido mediante accessibilityIncrement y accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — para elementos con valores que cambian frecuentemente (temporizador, indicador de progreso). VoiceOver no lee el valor en cada cambio, sino que hace una pausa. UIAccessibilityTraitAllowsDirectInteraction — para elementos con los que el usuario puede interactuar directamente (teclado, dibujo), sin pasar por los gestos de VoiceOver.
Un solo elemento puede tener varios traits simultáneamente: la combinación se establece mediante OR bit a bit (|). Ejemplo: un botón que está seleccionado actualmente — Button | Selected. VoiceOver anunciará: “Seleccionado. Filtrado por precio. Botón.”
Configuración de traits en código:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// O a través de una máscara:
filterButton.accessibilityTraits = [.button, .selected]
Para UIView personalizados donde el trait no se establece por defecto:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
Regla de combinación: no más de 3-4 traits por elemento. Los traits excesivos (por ejemplo, Button + Link + Header) hacen que el anuncio de VoiceOver sea demasiado largo y confuso. Según Apple, “cada propiedad adicional aumenta la carga cognitiva del usuario”.
En SwiftUI, los traits se establecen mediante los modificadores .accessibilityAddTraits() y .accessibilityRemoveTraits(). Ejemplo: Text(“Título”).font(.largeTitle).accessibilityAddTraits(.isHeader). El modificador .isHeader añade UIAccessibilityTraitHeader. Lista de traits de SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.
StaticText en lugar de Button — un control personalizado que visualmente parece un botón recibe el trait StaticText por defecto. VoiceOver no ofrece un gesto de activación, por lo que el usuario no puede “presionar” el elemento. Solución: establecer explícitamente .button.
Imagen sin trait — UIImageView con accesibilidad habilitada recibe el trait Image, incluso si en realidad es un botón para ampliar una foto. Asigne .button y Label “Ampliar foto.” Según WWDC 2023, “Deliver an Exceptional Accessibility Experience”, el 40% de las regresiones de accesibilidad en nuevas versiones de aplicaciones se deben precisamente a la falta de coincidencia del trait.
Header en cada elemento — el trait Header está destinado a encabezados estructurales de pantalla. Si cada UILabel se convierte en un encabezado, el rotor de VoiceOver en modo “Encabezados” se vuelve inútil: se detendrá en cada palabra.
Una causa común de pérdida del trait es la refactorización: un desarrollador reemplaza UIButton por UIControl para una visualización personalizada. UIButton automáticamente obtiene el trait Button, UIControl no. Después de la refactorización, debe establecer explícitamente accessibilityTraits = .button. Agregue una verificación en la revisión de código: “Si reemplazó UIButton por UIControl — verifique el trait.”
Para elementos con estado cambiante (por ejemplo, un botón de “Me gusta”), el trait debe cambiar dinámicamente. En estado “no me gusta” — Button, en estado “me gusta” — Button + Selected + Image (si hay un icono). VoiceOver cambia el anuncio: “Me gusta. Botón.” vs “Seleccionado. Me gusta. Botón.” Use accessibilityValue para transmitir el estado si el trait Selected no es suficiente. Relevante para botones de suscripción, favoritos, filtros e interruptores.
En Android, no hay un equivalente directo a los traits. En lugar de una máscara de bits, se utiliza:
Para Views personalizados en Android, debe sobrescribir 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
}
}
Los desarrolladores de Flutter deben usar el parámetro semanticsRole en el widget Semantics: button, header, image, link, textField y otros. Además, están disponibles semanticsLabel y semanticsHint — un equivalente completo de la tríada de iOS Label + Hint + Trait.
Para versiones web de aplicaciones móviles (PWA, WebView), se utiliza el atributo role de WAI-ARIA: role="button", role="heading", role="link". Este es un equivalente directo de accessibilityTraits. En aplicaciones híbridas, verifique que WebView pase los roles ARIA a la capa de accesibilidad nativa. Para ello, use el protocolo UIAccessibilityContainerDataTable en iOS o setAccessibilityDelegate en Android. Un WebView con JavaScript habilitado puede no pasar los roles ARIA correctamente — pruebe por separado.
En Android, puede agregar acciones personalizadas a AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK y ACTION_LONG_CLICK. Esto es equivalente al trait Button con gestos adicionales. Para deslizadores, use ACTION_SET_PROGRESS — equivalente a Adjustable. Para Spinner y DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE y ACTION_SET_TIME.
Xcode Accessibility Inspector es la herramienta principal para iOS: seleccione un elemento y vea el campo Traits. Mostrará la lista de traits establecidos. El rotor de VoiceOver con el modo “Elementos” permite navegar por todos los controles de la pantalla.
Prueba automatizada en Swift para verificar un trait:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["Enviar"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElement no proporciona acceso directo a los traits
// Verificación mediante activación de gesto
submitButton.tap()
XCTAssertTrue(app.staticTexts["Formulario enviado"].exists)
}
Verificación manual mediante VoiceOver: active VoiceOver, deslice hasta el elemento, toque dos veces: el elemento debería activarse si es un Button. Si el elemento no responde al doble toque, el trait es incorrecto. Use el gesto Rotor para cambiar entre modos (“Encabezados”, “Enlaces”, “Botones”) — cada modo mostrará solo los elementos con el trait correspondiente.
Antes de iOS 14, las pruebas unitarias no tenían acceso directo a accessibilityTraits. A partir de iOS 14, la propiedad está disponible: XCTAssertEqual(customButton.accessibilityTraits, .button). Utilice esto en pruebas unitarias para verificar controles personalizados. Se recomienda probar cada nuevo UIView personalizado para verificar la corrección del trait, especialmente después de refactorizaciones o cambios de clase padre.
Preguntas Frecuentes
Hasta 3-4 traits por elemento. Una mayor cantidad hace que el anuncio de VoiceOver sea redundante. Use combinaciones: Button + Selected, Header + StaticText.
UIAccessibilityTraitButton. iOS lo establece automáticamente para todas las instancias de UIButton. Si hereda de UIView y simula un botón, el trait debe establecerse manualmente.
Sí, UIAccessibilityTraitAdjustable — para elementos con valores ajustables (deslizadores, selectores, contadores). VoiceOver permite deslizar hacia arriba/abajo para cambiar el valor y lee el estado actual.
Use el modificador .accessibilityAddTraits(): Text(“Título”).font(.title).accessibilityAddTraits(.isHeader). El método funciona en iOS 14+.
VoiceOver asignará el trait None. El elemento no tendrá un rol: el screen reader solo leerá la Label sin indicar el tipo. El usuario no sabrá si hay un gesto de activación disponible.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también