Checklist de accesibilidad iOS para cumplir la EAA

La Ley Europea de Accesibilidad (EAA) es obligatoria desde el 28 de junio de 2025 para las apps de banca, comercio electrónico, transporte, medios y otros servicios digitales de consumo. En España la transpone la Ley 11/2023, cuyo régimen sancionador —por remisión al Real Decreto Legislativo 1/2013— contempla multas de hasta un millón de euros. Y no, el famoso «período de gracia hasta 2030» no existe para tu app: esa disposición transitoria solo cubre productos que ya se usaban legalmente antes de la fecha.

La norma técnica de referencia es la EN 301 549, que para software se apoya en los criterios WCAG 2.1 de nivel AA. El problema: WCAG está escrito pensando en la web, y traducirlo a una app iOS nativa —UIKit o SwiftUI— es justo la parte que casi nadie documenta en español.

Esta checklist hace esa traducción. Son 27 puntos verificables, organizados en seis bloques, cada uno con su criterio WCAG cuando aplica y una pista de la API de iOS implicada. Úsala para autoevaluar tu app antes de encargar una auditoría, o como lista de control durante el desarrollo.

Publicado: · Actualizado:
Desarrollador iOS de AtalayaSoft programando en su escritorio, con el código en pantalla

Cómo usar esta checklist

Cada punto es una afirmación que tu app cumple o no cumple; no hay medias tintas deliberadamente. Recorre los seis bloques con la app en la mano, VoiceOver activado y el tamaño de texto al máximo. Todo lo que falle es un hallazgo real que un auditor —o un usuario— también encontrará.

Esta checklist cubre lo verificable sin usuarios reales. No sustituye una auditoría completa: los criterios que dependen del contexto, las pruebas con personas usuarias de tecnologías de apoyo y la declaración de accesibilidad quedan fuera, y te contamos por qué al final de la página.

Resumen: bloques de la checklist, criterios WCAG 2.1 AA implicados y APIs de iOS relacionadas
Bloque Criterios WCAG APIs iOS implicadas
VoiceOver y etiquetado semántico 1.1.1 · 1.3.2 · 4.1.2 · 4.1.3 accessibilityLabel, accessibilityTraits, UIAccessibility.post
Texto y Dynamic Type 1.4.4 · 1.4.5 preferredFont(forTextStyle:), dynamicTypeSize
Color y contraste 1.4.1 · 1.4.3 · 1.4.11 Asset Catalog (variantes de contraste), accessibilityContrast (UIKit) / colorSchemeContrast (SwiftUI)
Interacción táctil y gestos 1.3.4 · 2.1.1 · 2.4.7 · 2.5.1 · 2.5.4 accessibilityCustomActions, Full Keyboard Access, focusable()
Formularios y flujos transaccionales 1.3.5 · 2.2.1 · 3.3.1 · 3.3.2 · 3.3.3 textContentType, keyboardType, anuncios de error
Medios, movimiento y contenido dinámico 1.2.2 · 1.2.5 · 2.3.1 · 2.3.3 AVPlayer (subtítulos), isReduceMotionEnabled

VoiceOver y etiquetado semántico

VoiceOver es el lector de pantalla de iOS y el primer filtro de cualquier auditoría. Si un elemento no está etiquetado, no tiene el rol correcto o se lee en un orden absurdo, la app es inutilizable para una persona ciega, por bonito que sea el resto.

  • Todos los elementos interactivos exponen una etiqueta de accesibilidad concisa y descriptiva; ninguna imagen funcional se anuncia como «imagen» a secas ni con el nombre del asset. (WCAG 1.1.1) accessibilityLabel en UIKit; .accessibilityLabel(_:) en SwiftUI. Las imágenes decorativas se ocultan con isAccessibilityElement = false / .accessibilityHidden(true).
  • Cada elemento anuncia su rol y estado reales: los botones se anuncian como botón, lo seleccionado como seleccionado, lo deshabilitado como atenuado. (WCAG 4.1.2) accessibilityTraits (.button, .selected, .notEnabled); en SwiftUI, .accessibilityAddTraits(_:).
  • El orden de lectura con VoiceOver sigue la lógica visual y funcional de la pantalla, no el orden accidental de la jerarquía de vistas. (WCAG 1.3.2) accessibilityElements en el contenedor; .accessibilitySortPriority(_:) en SwiftUI.
  • Los elementos que forman una unidad de significado (icono + título + subtítulo de una celda) se agrupan y se leen como un único elemento, no como tres paradas sueltas. shouldGroupAccessibilityChildren / elemento contenedor con label compuesta; .accessibilityElement(children: .combine) en SwiftUI.
  • Los cambios dinámicos de contenido (resultados cargados, errores, actualizaciones en pantalla) se anuncian a VoiceOver sin que el usuario tenga que ir a buscarlos. (WCAG 4.1.3) UIAccessibility.post(notification: .announcement, …) / .layoutChanged; AccessibilityNotification.Announcement en SwiftUI.
  • Las acciones disponibles por gesto contextual (deslizar para borrar, mantener pulsado) están también expuestas al rotor como acciones personalizadas. accessibilityCustomActions; .accessibilityAction(named:) en SwiftUI.

Texto y Dynamic Type

Dynamic Type permite al usuario elegir el tamaño de texto de todo el sistema. Una app que lo ignora —o que se rompe al activarlo— excluye a una parte enorme de sus usuarios, y es de lo primero que comprueba cualquier auditor porque se detecta en segundos.

  • Todo el texto de la app escala con Dynamic Type; no hay tamaños de fuente fijos en pantallas de contenido. (WCAG 1.4.4) UIFont.preferredFont(forTextStyle:) + adjustsFontForContentSizeCategory = true; en SwiftUI los estilos de Font (.body, .headline…) escalan por defecto.
  • No hay texto informativo rasterizado dentro de imágenes; todo el texto es texto real que escala y que VoiceOver puede leer. (WCAG 1.4.5) componer texto + imagen en la vista en lugar de assets con texto incrustado.
  • Con el texto ampliado, los layouts crecen en vez de romperse: nada se trunca con «…» perdiendo información, nada se solapa, y las alturas de celda son autoajustables. Auto Layout sin alturas fijas, UITableView.automaticDimension; en SwiftUI, evitar .lineLimit restrictivos y frames rígidos.
  • La app es funcional en los tamaños de accesibilidad (AX1–AX5), no solo en los cinco tamaños estándar; donde el layout horizontal deja de caber, se reorganiza en vertical. traitCollection.preferredContentSizeCategory.isAccessibilityCategory; @Environment(\.dynamicTypeSize) y ViewThatFits en SwiftUI.

Color y contraste

El contraste insuficiente es el fallo más común y el más barato de arreglar. Y el color nunca puede ser el único vehículo de un significado: lo que hoy es «rojo = error» para ti, para una persona con daltonismo es simplemente otro gris.

  • El texto normal alcanza un contraste mínimo de 4,5:1 sobre su fondo, y el texto grande y los componentes de interfaz llegan al menos a 3:1 — también en modo oscuro. (WCAG 1.4.3 · 1.4.11) colores semánticos del sistema o Asset Catalog con variantes claro/oscuro; verificar con el Accessibility Inspector de Xcode.
  • Ningún estado o información se comunica solo con color: los errores, lo seleccionado y lo activo llevan además icono, texto o cambio de forma. (WCAG 1.4.1) revisión de diseño; SF Symbols como refuerzo no cromático.
  • La app responde a los ajustes de contraste del sistema y no se rompe con Smart Invert: las fotos y el contenido ya oscuro no se invierten. variantes de alto contraste en Asset Catalog, UIAccessibility.isDarkerSystemColorsEnabled o traitCollection.accessibilityContrast en UIKit; accessibilityIgnoresInvertColors y @Environment(\.colorSchemeContrast) en SwiftUI.

Interacción táctil y gestos

Que un control exista no significa que se pueda usar. Objetivos táctiles minúsculos, gestos que exigen dos manos o precisión de cirujano, y pantallas que solo responden a agitar el teléfono son barreras directas para personas con movilidad reducida.

  • Todos los objetivos táctiles miden al menos 44×44 puntos, aunque su representación visual sea menor. ampliar el área con insets; en UIKit, sobreescribir point(inside:with:) o hitTest(_:with:) si hace falta; HIG de Apple como referencia.
  • Toda función accionada por un gesto complejo o multipunto (pellizcar, arrastrar, deslizar con dos dedos) tiene una alternativa de un solo toque. (WCAG 2.5.1) botones alternativos visibles o accessibilityCustomActions.
  • Ninguna función depende de mover el dispositivo (agitar, inclinar) sin una alternativa en pantalla, y la respuesta al movimiento puede desactivarse. (WCAG 2.5.4) alternativa de UI a CMMotionManager / gesto de agitar; respetar los ajustes del sistema.
  • La app es completamente operable con Full Keyboard Access y Switch Control: todo lo interactivo recibe el foco, el foco es visible y no hay trampas de foco. (WCAG 2.1.1 · 2.4.7) probar con Full Keyboard Access activado; UIKeyCommand, canBecomeFocused; .focusable() y @FocusState en SwiftUI.
  • La app funciona en vertical y en horizontal; no se bloquea a una sola orientación salvo cuando es esencial para la tarea (por ejemplo, la captura de un cheque). (WCAG 1.3.4) no restringir supportedInterfaceOrientations sin justificación; en SwiftUI, layouts que se adaptan al tamaño disponible en lugar de asumir una orientación.

Formularios y flujos transaccionales

La EAA aplica sobre todo a servicios con los que el usuario hace cosas: comprar, contratar, pagar, reservar. De poco sirve un onboarding impecable si el formulario de pago es infranqueable con VoiceOver. Este bloque es el que decide si tu app cumple donde de verdad importa.

  • Todos los campos tienen etiqueta visible y persistente; el placeholder nunca es la única etiqueta, porque desaparece al escribir. (WCAG 3.3.2) label visible asociada + accessibilityLabel en el campo; en SwiftUI, TextField con LabeledContent o label explícita.
  • Cuando la validación falla, el error se identifica y se describe en texto junto al campo afectado, y VoiceOver lo anuncia en el momento. (WCAG 3.3.1) mensaje de error como texto asociado al campo + UIAccessibility.post(notification: .announcement, …).
  • Los errores recuperables ofrecen una sugerencia de corrección concreta («el IBAN debe tener 24 caracteres»), no un genérico «datos inválidos». (WCAG 3.3.3) mensajes de validación específicos por regla.
  • Cada campo declara su propósito y su teclado: el email saca teclado de email y se autorrellena, el código SMS se sugiere solo, la contraseña funciona con gestores. (WCAG 1.3.5) textContentType (.emailAddress, .oneTimeCode, .password…) + keyboardType.
  • Ningún paso del flujo transaccional caduca sin aviso ni posibilidad de ampliar el tiempo, y el flujo completo —del carrito a la confirmación— es operable de principio a fin solo con VoiceOver. (WCAG 2.2.1) avisos antes de expirar sesión con opción de extender; prueba manual del flujo entero con VoiceOver.

Medios, movimiento y contenido dinámico

El contenido audiovisual y las animaciones tienen sus propias reglas: lo que no se oye necesita subtítulos, lo que no se ve necesita descripción, y el movimiento que a ti te parece elegante puede marear —literalmente— a parte de tus usuarios.

  • Todo el contenido de vídeo pregrabado con audio ofrece subtítulos, y el reproductor respeta los subtítulos y ajustes de estilo del sistema. (WCAG 1.2.2) pistas de subtítulos en el asset; AVPlayer los gestiona con los ajustes del usuario (MACaptionAppearance).
  • El vídeo pregrabado con audio ofrece audiodescripción cuando hay información visual que no está ya presente en la banda sonora; a nivel AA, una transcripción no sustituye a la audiodescripción. (WCAG 1.2.5) pista de audiodescripción en el asset (AVMediaCharacteristic.describesVideoForAccessibility); la transcripción accesible es un complemento, no el sustituto.
  • La app respeta el ajuste «Reducir movimiento»: los parallax, zooms y transiciones grandes se sustituyen por fundidos o se eliminan. (WCAG 2.3.3) Recomendado UIAccessibility.isReduceMotionEnabled; @Environment(\.accessibilityReduceMotion) en SwiftUI.
  • Nada en la app parpadea más de tres veces por segundo, incluidos vídeos, animaciones de carga y efectos promocionales. (WCAG 2.3.1) revisión de animaciones y contenido; sin API específica — es una regla de diseño.

Qué no cubre esta checklist (y cuándo necesitas una auditoría)

Ser honestos aquí es parte del trabajo: pasar estos 27 puntos significa que has eliminado las barreras más frecuentes y objetivas, no que tu app «cumple la EAA». Hay tres cosas que una checklist no puede darte:

  • Pruebas con personas usuarias reales de tecnologías de apoyo — el orden de lectura puede ser técnicamente correcto y aun así resultar agotador de usar; eso solo lo revela alguien que navega con VoiceOver a diario.
  • Los criterios que dependen del contexto de tu app — qué es «texto grande» en tu diseño, qué alternativa tiene sentido para tu gesto, qué flujos son servicios afectados por la EAA y cuáles no.
  • La parte formal del cumplimiento — la evaluación documentada frente a EN 301 549 y la información de accesibilidad que el servicio debe poner a disposición del público.

¿Tu app pasa la checklist… o no del todo?

Hacemos auditorías de accesibilidad específicas de iOS nativo —no adaptaciones de auditoría web— con remediación incluida si la necesitas. Desde un diagnóstico inicial hasta el acompañamiento continuo, con precios publicados.

Ver el servicio de accesibilidad iOS (EAA)

Preguntas frecuentes

No. El Accessibility Inspector detecta una parte de los problemas objetivos —etiquetas ausentes, contraste, áreas táctiles— pero no evalúa si el orden de lectura tiene sentido, si los anuncios dinámicos llegan cuando deben o si el flujo de pago es completable con VoiceOver. Es una gran herramienta de desarrollo y un pésimo certificado de cumplimiento.

La EAA regula productos y servicios ofrecidos a consumidores, así que una app exclusivamente interna queda en general fuera de su ámbito. Cuidado con dos matices: si la app la usan clientes finales en algún punto, entra; y las obligaciones de accesibilidad laboral y de igualdad de trato existen por otras vías legales aunque la EAA no aplique.

En cada release que toque interfaz, como parte del flujo normal de QA — la accesibilidad se rompe igual que cualquier otra funcionalidad, y un refactor de una pantalla puede deshacer etiquetas y agrupaciones sin que nadie lo note. Como mínimo, una pasada completa por versión mayor de iOS: cada septiembre cambian cosas.

No: la EAA y la EN 301 549 son neutrales respecto a la tecnología; lo que se exige es el resultado. Cambia el cómo: SwiftUI trae mejores valores por defecto (Dynamic Type, roles), pero ninguno de los dos frameworks cumple solo, y las apps reales suelen mezclar ambos — lo que obliga a verificar que la experiencia con VoiceOver es coherente al cruzar de un mundo a otro.
¿Hablamos?

Hablemos de tu reto iOS

Reserva 30 minutos con Francisco José García Navarro, Arquitecto iOS Senior, para revisar tu proyecto. Sin compromiso.