Focus Order es la secuencia en la que los elementos de la interfaz reciben el foco al navegar con un teclado, Switch Control, VoiceOver o TalkBack. En aplicaciones móviles, el orden de foco determina cómo el usuario se desplaza entre los controles mediante gestos o botones. Según W3C WCAG 2.2, Success Criterion 2.4.3, 2023, el foco debe seguir un orden lógico que preserve el significado del contenido. La violación de este principio es una de las causas comunes de fracaso en una auditoría de accesibilidad.
Puntos clave
Focus Order es la secuencia en la que el usuario se desplaza entre los elementos interactivos utilizando métodos de entrada alternativos: teclado (Tab), Switch Control (paso a paso), VoiceOver (deslizar derecha/izquierda) o TalkBack. A diferencia de un ratón o pantalla táctil, donde el usuario selecciona un elemento directamente, la navegación por foco es lineal — cada paso mueve el foco al siguiente elemento.
Según Apple HIG, 2024, VoiceOver utiliza el orden de los elementos en el árbol de accesibilidad, que se construye en función de la ubicación visual: esquina superior izquierda → esquina inferior derecha. Si la pantalla tiene un diseño complejo (columnas, Grid, ZStack), el árbol puede no coincidir con el orden visual.
Principio de WCAG 2.4.3: “Si una página web puede navegarse secuencialmente por secciones y el orden de foco afecta el significado, entonces el foco debe seguir un orden que preserve el significado y la operatividad”. Excepción: contenido dinámico donde el foco puede saltar para llamar la atención (alertas, ventanas modales).
Un usuario de Switch Control (personas con discapacidades motoras) se desplaza por los elementos automáticamente — ciclo tras ciclo. Si el orden está roto, el usuario tarda 3 veces más en completar el formulario. Según Deque University, 2024, un Focus Order correcto reduce el tiempo de completado del formulario en un 60% para usuarios de tecnologías de asistencia.
Atención especial — ventanas modales. Después de abrir una modal, el foco debe moverse inmediatamente al primer elemento interactivo dentro de la modal (normalmente un botón “Cerrar” o “Confirmar”). Al cerrar — volver al elemento que activó la modal. Este es un requisito de WCAG 2.4.3 y también un error común.
En iOS, VoiceOver construye automáticamente el orden basándose en la geometría: los elementos se ordenan por Y, luego por X. Para pantallas con estructura compleja, este orden puede ser incorrecto — el desarrollador debe intervenir.
Herramientas principales:
Ejemplo de configuración de orden personalizado para una tarjeta de producto:
class ProductCardView: UIView {
let titleLabel = UILabel()
let priceLabel = UILabel()
let buyButton = UIButton()
override var accessibilityElements: [Any]? {
get {
return [titleLabel!, priceLabel!, buyButton!]
}
set {}
}
}
Para el movimiento programático del foco después de una acción:
UIAccessibility.post(
notification: .layoutChanged,
argument: newlyAddedItem
)
La propiedad shouldGroupAccessibilityElement es útil para tarjetas en colecciones. Si se establece en true en la tarjeta padre, VoiceOver percibe toda la tarjeta como un solo elemento. El usuario puede tocar dos veces para activar la tarjeta completa, o configurar el rotor para la navegación interna. Recomendado para UICollectionViewCell y UITableViewCell.
En Android, TalkBack también utiliza el orden geométrico, pero se da prioridad a los atributos explícitos nextFocus*. Estos atributos se establecen en XML o programáticamente:
| Atributo | Propósito | Ejemplo |
|---|---|---|
| nextFocusDown | Elemento al navegar hacia abajo | @+id/field_email |
| nextFocusUp | Elemento al navegar hacia arriba | @+id/field_name |
| nextFocusLeft | Elemento a la izquierda | @+id/btn_back |
| nextFocusRight | Elemento a la derecha | @+id/btn_next |
Ejemplo para un formulario de registro:
<EditText
android:id="@+id/field_email"
android:nextFocusDown="@+id/field_password" />
<EditText
android:id="@+id/field_password"
android:nextFocusDown="@+id/btn_submit" />
Para RecyclerView, el orden de foco es dinámico — lo determina el adaptador. Si las celdas tienen una estructura compleja, establezca descendantFocusability = “beforeDescendants” y defina el orden en el nodo del elemento de la lista. Para Jetpack Compose, el orden de foco se establece mediante Modifier.focusOrder() y FocusOrder. Prioridad: previous (hijo), next (siguiente), clave personalizada.
Si un elemento es demasiado pequeño para el foco (menor de 44pt), aumente el área de impacto mediante TouchDelegate en iOS o minWidth/minHeight en Android. Según Google Material Design, 2024, el área táctil mínima es de 48×48dp. VoiceOver y TalkBack se centran en el cuadro delimitador del elemento. Los elementos de menos de 30pt pueden ser inaccesibles para el foco gestual — el usuario físicamente no puede tocarlos.
Foco saltarín — cuando después de una acción (por ejemplo, eliminar un elemento) el foco se mueve al inicio de la lista o al botón “Volver” del sistema. El usuario de VoiceOver pierde el contexto. Solución: mover programáticamente el foco al elemento más cercano al eliminado.
Foco invisible — un elemento recibe foco pero no hay indicador visual (los usuarios de teclado no ven dónde están). En iOS, verifique UIAccessibility.isVoiceOverRunning para indicadores personalizados. Según Deque University, 2024, el foco invisible es la segunda causa más común de fracaso en una auditoría de accesibilidad.
Modales — el foco permanece en el contenido de fondo después de abrir una modal. En iOS, la vista modal captura automáticamente el foco si se establece modalPresentationStyle = .pageSheet. En Android, use setFocusable(true) en el contenedor del diálogo.
El problema inverso: el foco se queda atascado dentro de una modal y no puede salir (excepto cerrándola). Esto es aceptable solo para ventanas modales — el usuario debe cerrar la ventana intencionadamente. Para pantallas normales, la trampa de foco es un error crítico. Solución: asegúrese de que el último elemento de la modal (el botón “Cerrar”) devuelva el foco.
Para pantallas personalizadas (mapas, lienzos, juegos) el orden geométrico automático no es aplicable. El desarrollador debe construir el árbol de accesibilidad manualmente. En iOS, para esto se sobrescribe el método UIAccessibilityContainer.
Ejemplo para un lienzo personalizado:
class CanvasView: UIView {
var shapes: [ShapeView] = []
override var accessibilityElements: [Any]? {
get {
// Ordenar formas por Z-index, no por geometría
return shapes.sorted { $0.zIndex < $1.zIndex }
}
set {}
}
}
En Android, para una View personalizada, sobrescriba onInitializeAccessibilityNodeInfo:
override fun onInitializeAccessibilityNodeInfo(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(info)
info.addChild(firstElement)
info.addChild(secondElement)
info.isFocusable = true
}
Para listas dinámicas (chat, feed de noticias), después de añadir un elemento, mueva el foco al primer elemento nuevo. En iOS: UIAccessibility.post(notification: .layoutChanged, argument: newMessage). En Android: sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED).
iOS determina automáticamente el área de foco basándose en el marco del elemento. Si un elemento tiene una transformación (transform, rotation), VoiceOver puede enfocar el área incorrecta. Establezca explícitamente accessibilityFrame en coordenadas de pantalla: element.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(element.bounds, in: element). Esto garantiza que VoiceOver resalte el área correcta.
Para pantallas animadas (UIKit Dynamics, Lottie, SpriteKit), el foco programático es especialmente importante. VoiceOver no puede construir un árbol de accesibilidad para elementos que se mueven dinámicamente. Establezca isAccessibilityElement = false en los contenedores de animación y true solo en los elementos interactivos del interior.
Pruebas manuales: active VoiceOver (iOS) o TalkBack (Android), deslice hacia la derecha por toda la secuencia. El foco debe seguir el orden visual — de izquierda a derecha, de arriba abajo. Cada elemento interactivo debe recibir el foco exactamente una vez.
Pruebas automatizadas son difíciles pero posibles:
func testKeyboardFocusOrder() {
let app = XCUIApplication()
app.launch()
app.textFields["Email"].tap()
// Tab — solo con teclado físico
}
Para Android, use el Accessibility Testing Framework:
@Test
fun testFocusOrder() {
onView(withId(R.id.fieldEmail))
.check(matches(isFocusable()))
onView(withId(R.id.fieldEmail))
.perform(focus())
onView(withId(R.id.fieldPassword))
.check(matches(isFocused()))
}
El método más fiable es una prueba de escenario de UI: rellene el formulario paso a paso (Email → Contraseña → Enviar), comprobando que cada paso se completa correctamente. Si el orden de foco está roto, el escenario fallará al intentar interactuar con un elemento fuera de foco.
La herramienta Accessibility Inspector en Xcode muestra el árbol de accesibilidad completo. Puede recorrer los elementos en el orden de VoiceOver y ver la ruta de foco exacta. Use la pestaña “Audit” para la detección automática de violaciones de Focus Order.
Preguntas frecuentes
WCAG 2.4.3 (Focus Order) es un criterio de éxito de nivel A. Requiere que el orden de foco preserve el significado del contenido durante la navegación secuencial. La violación se considera crítica y bloquea la certificación.
Los elementos ocultos deben tener isAccessibilityElement = false en iOS o visibility = gone/invisible en Android. Cuando aparezcan, mueva programáticamente el foco mediante UIAccessibility.post(notification: .layoutChanged).
iOS gestiona mediante accessibilityElements y shouldGroupAccessibilityElement, Android mediante atributos nextFocus* y AccessibilityNodeInfo. El principio es el mismo: orden geométrico por defecto con posibilidad de sobrescribirlo.
Establezca descendantFocusability = “beforeDescendants” en el elemento raíz y configure el orden en el adaptador mediante onInitializeAccessibilityNodeInfo para cada celda.
Conecte un teclado físico mediante Bluetooth o USB. En iOS pulse Tab para mover el foco. En Android active TalkBack y use las teclas Tab y las flechas.
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