📆

A11y en SwiftUI: Agrupando que es gerundio

Vamos a agrupar elementos de forma accesible en SwiftUI

¡Bienvenidos a esta serie de artículos sobre accesibilidad en SwiftUI! Durante las próximas semanas, exploraremos diferentes aspectos de la A11y para crear aplicaciones verdaderamente inclusivas. En este primer artículo, nos centraremos en una técnica fundamental: la agrupación de elementos.

El Escenario: Una Lista de Libros 📚

Imagina que estás desarrollando una app de libros. Tienes una lista donde cada elemento muestra:

  • 🌆 Una portada del libro
  • 📗 El título
  • 📝 El autor
  • 📆 El año de publicación
  • ⭐️ Una valoración con estrellas

Suena simple, ¿verdad? Pero cuando activamos VoiceOver, la cosa se complica…

Houston, Tenemos un Problema 🚨

captura de pantalla de la lista de libros donde se va resaltando en orden todos los elementos de A11y, empieza señalando la imagen con la portada del libro, luego el titulo, autor, año y por ultimo la valoración

Si no configuramos la accesibilidad, VoiceOver tratará cada elemento de la UI como un elemento independiente. Esto significa que nuestros usuarios tendrán que navegar por cinco elementos diferentes para entender la información de un solo libro. No es la mejor experiencia, ¿verdad?

La Pregunta Clave: ¿Qué es Realmente Importante? 🤔

Antes de empezar a codificar, debemos hacernos algunas preguntas:

  1. ¿La imagen de la portada aporta información única que no está en el texto?
  2. ¿Necesitamos que VoiceOver lea los emojis decorativos?
  3. ¿Cómo percibe visualmente el usuario esta información?

La respuesta nos lleva a una conclusión: visualmente percibimos cada libro como una unidad, no como elementos separados. ¡Hagamos que la experiencia con VoiceOver refleje esto!

Solución 1: El Camino Rápido ⚡️

La forma más directa de resolver esto es agrupar todo en un único elemento accesible:

BookView(book: book)
    .accessibilityElement()
    .accessibilityLabel("\(book.title) escrito por \(book.author) en \(book.year), \(book.stars) de 5")

Solución 2: El Enfoque Granular 🎯

Para vistas más complejas, podemos optar por un enfoque más estructurado:

struct BookInfoView: View {
    let book: Book

    var body: some View {
        HStack {
            Image(book.image)
                .accessibilityHidden(true)  // La imagen es decorativa

            VStack {
                Text(book.title)
                // El label por defecto funciona bien aquí

                Text(book.author)
                    .accessibilityLabel("escrito por \(book.author)")

                Text("📆 \(book.year)")
                    .accessibilityLabel("en \(book.year)")

                RatingView(rating: book.stars)
                    .accessibilityLabel("\(book.stars) estrellas de 5")
            }
            .accessibilityElement(children: .combine)
            // Combina todos los labels en una única lectura fluida
        }
    }
}

El Resultado: Una Experiencia Unificada ✨

captura de pantalla de la lista de libros donde se resalta en verde cada elemento de A11y, ahora todo lo relacionado con un mismo libro es un único elemento de A11y

Ahora, VoiceOver trata cada libro como una unidad coherente, ofreciendo una experiencia mucho más natural y eficiente.

¿Cuál Elegir? 🤷‍♀️

  • Solución 1: Perfecta para componentes simples y directos
  • Solución 2: Ideal para vistas complejas donde necesitas más control sobre cada elemento

Conclusión y Próximos Pasos 🎯

La accesibilidad no es un extra, es una necesidad fundamental. En este artículo hemos visto cómo la agrupación adecuada de elementos puede mejorar significativamente la experiencia de usuarios que dependen de VoiceOver.

En el próximo artículo de la serie, exploraremos cómo manejar los traits en SwiftUI. ¡No te lo pierdas!


¿Te ha resultado útil este artículo? ¿Tienes alguna duda, pregunta, consejo sobre accesibilidad en SwiftUI? Pásate por BlueSky y hablamos

Vamos a hablar de accesibilidad con SwiftUI en el blog. Agrupando que es gerundio. dianait.blog/blog/a11y-en...

[image or embed]

— Diana Hernández (@dianait.dev) 23 February 2025 at 16:28