Lección 10 de 10 · Caso práctico · 25 min · Revisado el 26/10/2026
Caso práctico, ejercicios y lista de comprobación
Esta lección reúne el curso en un caso realista, resuelto paso a paso, seguido de ejercicios de autoevaluación y una lista de comprobación para tus proyectos. Los nombres son ficticios; los problemas son los que aparecen una y otra vez en apps reales.
Caso práctico: la app municipal de incidencias
Un ayuntamiento de tamaño medio publica «Mi Barrio», una app nativa (SwiftUI en iOS y Jetpack Compose en Android) para comunicar incidencias en la vía pública: una farola fundida, un bache, un contenedor desbordado. El flujo tiene cuatro pantallas: categoría (cuadrícula de iconos), ubicación (mapa con un punto que se arrastra), detalles (foto, descripción y datos de contacto) y confirmación. Al ser una app de una administración pública, está sujeta al RD 1112/2018 (PDF, se abre en una ventana nueva) y debe cumplir la EN 301 549 v3.2.1 (PDF, se abre en una ventana nueva), cuyo capítulo 11 recoge los criterios WCAG 2.1 de nivel A y AA aplicados al software; el equipo trabaja ya con WCAG 2.2, que los incluye y añade otros como 2.5.7, 2.5.8 y 3.3.8. Un vecino que usa TalkBack informa por el mecanismo de comunicación de que «no hay forma de enviar una incidencia».
Paso 1: recorrer la tarea con tecnologías de apoyo
Antes de mirar el código, el equipo recorre el flujo completo con TalkBack y VoiceOver, con el texto al máximo y con un teclado Bluetooth, y anota lo que oye y lo que no puede hacer. Resultado:
| Pantalla | Problema detectado | Criterios |
|---|---|---|
| Categoría | Los iconos se anuncian como «botón» sin nombre; el texto bajo cada icono es una imagen. | 1.1.1, 4.1.2, 2.5.3 |
| Ubicación | Solo se puede fijar el punto arrastrando el mapa; el mapa atrapa el foco del teclado. | 2.5.7, 2.1.1, 2.1.2 |
| Detalles | Campos con placeholder como única etiqueta; el teléfono no se autocompleta; el error solo pone el borde en rojo. | 3.3.2, 1.3.5, 3.3.1, 1.4.1 |
| Detalles | Con texto al 200 %, el botón «Enviar» queda tapado por el teclado y la etiqueta de la foto se trunca. | 1.4.4, 2.4.11 |
| Confirmación | Aparece una capa superpuesta propia; TalkBack sigue leyendo el formulario de detrás y no anuncia el número de incidencia. | 2.4.3, 4.1.3 |
| Todas | La app está bloqueada en vertical; las pantallas no tienen título anunciado. | 1.3.4, 2.4.2 |
Paso 2: priorizar
Primero lo que bloquea la tarea: la ubicación por arrastre y la confirmación inaccesible impiden enviar la incidencia, y los iconos sin nombre impiden siquiera empezar. Después lo que la dificulta: etiquetas, errores, texto grande y orientación. Es la misma lógica de impacto por frecuencia que viste en el nivel 4.
Paso 3: corregir
- Categorías: cada celda pasa a ser un
ButtonconLabel("Alumbrado público", systemImage: …)en iOS y, en Android, un elementoselectableconTextreal bajo el icono decorativo (contentDescription = null). El texto visible es ya el nombre accesible y Voice Control puede decirlo. - Ubicación: se añaden un campo «Escribe una dirección» con sugerencias, un botón «Usar mi ubicación» y los botones de zoom. El mapa queda como apoyo visual; la dirección elegida se muestra en texto y se anuncia. Se revisa que Tab salga del mapa.
- Detalles: etiquetas visibles con
OutlinedTextField(label = …)y texto encima del campo en iOS;textContentType(.telephoneNumber)ycontentType = ContentType.PhoneNumber; errores en texto consemantics { error(…) }y anuncio del número de errores; la foto tiene un botón «Añadir foto (opcional)» con nombre y estado. - Texto grande: la pantalla se envuelve en un contenedor desplazable que respeta los insets del teclado; la etiqueta de la foto admite varias líneas.
- Confirmación: la capa propia se sustituye por una pantalla nueva con título «Incidencia enviada», que recibe el foco y anuncia «Incidencia 2026-0412 registrada. Recibirás un aviso cuando se resuelva».
- Global: se elimina el bloqueo de orientación y cada destino declara
navigationTitleopaneTitle.
Paso 4: proteger y verificar
Se crea un componente CampoFormulario que exige etiqueta y tipo de contenido, se añaden pruebas de semántica para las celdas de categoría y una auditoría automática en las pruebas de UI de ambas plataformas. Finalmente, se repite el recorrido del paso 1 con TalkBack, VoiceOver, texto al máximo, teclado y Switch Access, y se contacta con el vecino que avisó para que lo compruebe. La declaración de accesibilidad de la app se actualiza con el resultado.
Ejercicios de autoevaluación
1. En iOS, una pantalla de ajustes muestra una lista de interruptores propios: un texto y una cápsula que cambia de gris a verde al tocarla. VoiceOver lee solo el texto. ¿Qué harías?
Ver solución
Sustituir el control por un Toggle (o UISwitch) con un estilo visual propio, que aporta rol, estado («activado/desactivado») y activación. Si no es posible, agrupar texto y cápsula en un elemento con el rasgo de botón o .isToggle (iOS 17) y accessibilityValue con el estado. Además, el estado no puede depender solo del color (1.4.1): añade una marca o un texto.
2. Una app de banca pide el código de verificación en seis campos de una cifra y no permite pegar. ¿Qué criterios están en riesgo y cuál es la solución?
Ver solución
3.3.8 Autenticación accesible (mínimo), porque obliga a transcribir el código sin ayuda, y 4.1.2 si los campos no tienen nombre. La solución es un único campo etiquetado («Código de 6 cifras») con .oneTimeCode en iOS y la pista smsOTPCode o las API de SMS en Android, que admita pegado y autocompletado.
3. Al cerrar una hoja inferior de filtros en Compose, TalkBack vuelve al principio de la pantalla. ¿Cómo lo corriges?
Ver solución
Usar ModalBottomSheet (que oculta el fondo) y, al cerrarlo, devolver el foco al botón «Filtros» que la abrió. Compruébalo con TalkBack activo, porque mover el foco de entrada con FocusRequester no siempre mueve el foco de accesibilidad; si es necesario, anuncia el resultado («24 resultados») como live region.
4. Las pruebas de UI de iOS localizan el botón de búsqueda con app.buttons["search_btn"], y ese texto es su accessibilityLabel. ¿Qué problema hay y qué cambiarías?
Ver solución
VoiceOver y Voice Control usan esa etiqueta, así que la persona oye «search btn» y no puede decir «Pulsar Buscar». Asigna accessibilityIdentifier = "search_btn" para las pruebas y accessibilityLabel = "Buscar" para las personas, y añade una aserción que compruebe la etiqueta.
5. El gráfico de consumo de una app de una comercializadora eléctrica es una imagen sin descripción. Propón la solución mínima y la recomendable.
Ver solución
Mínima: un texto alternativo que resuma la información clave (total, máximo y mínimo) y una tabla accesible con los datos (1.1.1). Recomendable: además, un AXChartDescriptor en iOS para usar Audio Graphs, nodos virtuales por barra en Android y series diferenciadas por algo más que el color (1.4.1, 1.4.11).
Lista de comprobación
- Cada control tiene nombre, rol, estado y valor en la API de la plataforma.
- Los botones de icono tienen nombre, y el nombre contiene el texto visible.
- Lo decorativo está oculto y las celdas compuestas son un solo elemento.
- Cada pantalla tiene un título anunciado y encabezados por secciones.
- El orden de recorrido sigue la lógica de la tarea.
- Los diálogos reciben el foco, ocultan el fondo y lo devuelven al cerrar.
- Todo gesto, arrastre o movimiento tiene alternativa de un toque.
- Los objetivos táctiles miden al menos 44 pt o 48 dp.
- El texto escala al máximo sin truncarse y la app gira libremente.
- Los campos tienen etiqueta visible y propósito de entrada declarado.
- Los errores se muestran en texto y se anuncian.
- El inicio de sesión admite gestores de contraseñas, pegado y biometría con alternativa.
- Mapas, gráficos y carruseles tienen alternativa accesible.
- Funciona con VoiceOver, TalkBack, Switch Access y teclado externo.
- Lint, pruebas de semántica y auditoría automática están en la integración continua.
Para seguir aprendiendo
Comprueba lo que has aprendido
Esta lección se puede leer libremente. Para hacer su test de 3 preguntas, los ejercicios prácticos con páginas reales, guardar tu avance y optar al examen y al certificado, matricúlate en el curso.
Matricularme · nivel 6 (119 €)