Lliçó 3 de 10 · 11 min de lectura · Revisada el 26/10/2026
iOS: VoiceOver, Dynamic Type, API d'accessibilitat d'UIKit i SwiftUI, Voice Control
Estàs llegint una lliçó oberta. Matricula't per fer els tests i els exercicis, desar el teu progrés i aconseguir el certificat.
Matricula-m'hi · nivell 6 (119 €)iOS té una de les piles d'accessibilitat més madures del mercat, i els components estàndard d'UIKit i SwiftUI són accessibles per defecte. Els problemes apareixen amb vistes personalitzades, imatges com a botons, agrupacions incorrectes i mides fixes. Aquesta lliçó recorre les eines de l'usuari (VoiceOver, Dynamic Type, Voice Control) i les API amb què el desenvolupador descriu la interfície.
VoiceOver en 3 minuts per a desenvolupadors
Activa VoiceOver amb una triple pulsació del botó lateral (configura-ho a Configuració > Accessibilitat > Drecera d'accessibilitat) o demanant-ho a Siri. Gestos essencials: un toc per seleccionar i sentir, lliscar a la dreta/esquerra per anar a l'element següent/anterior, doble toc per activar, tres dits per desplaçar, dos dits en «Z» per tornar enrere, rotor (girar dos dits) per canviar entre encapçalaments, enllaços, controls de formulari, etc. Doble toc i mantenir permet arrossegar. El que VoiceOver llegeix de cada element, en ordre: etiqueta (label), valor, trets (traits: botó, encapçalament, seleccionat, ajustable…) i pista (hint). Si l'element té accions personalitzades, s'anuncia «accions disponibles» i es recorren lliscant amunt/avall.
L'API d'UIAccessibility a UIKit
Cada UIView exposa propietats que alimenten l'arbre d'accessibilitat:
isAccessibilityElement: si la vista és un element (true per defecte als controls i a les etiquetes; false a les vistes contenidores).accessibilityLabel: el nom. Breu, sense incloure el tipus («Envia», no «Botó envia»).accessibilityValue: el valor actual (el percentatge d'un control lliscant, el text d'un camp, «3 de 5»).accessibilityTraits:.button,.link,.header,.image,.selected,.notEnabled,.adjustable,.updatesFrequently…accessibilityHint: què passarà («Obre el detall del moviment»). Opcional, i l'usuari pot desactivar les pistes.accessibilityElementsen un contenidor per definir l'ordre dels fills;shouldGroupAccessibilityChildrenperquè VoiceOver els llegeixi junts.accessibilityCustomActions: accions alternatives als gestos (eliminar, arxivar, moure).accessibilityUserInputLabels: noms alternatius per a Voice Control.
Exemple: una cel·la de moviment bancari composta per tres etiquetes i una icona. Sense intervenció, VoiceOver llegeix quatre elements separats («Supermercat», «−42,30», «15 de març», «imatge»). El correcte és agrupar-los en un sol element amb una etiqueta composta i accions:
final class MovimientoCell: UITableViewCell {
func configurar(con m: Movimiento) {
conceptoLabel.text = m.concepto
importeLabel.text = m.importeFormateado
fechaLabel.text = m.fechaFormateada
iconoView.image = m.icono
// Un sol element accessible per cel·la
isAccessibilityElement = true
accessibilityLabel = "\(m.concepto), \(m.importeAccesible), \(m.fechaLarga)"
accessibilityTraits = .button
accessibilityHint = "Obre el detall del moviment"
accessibilityCustomActions = [
UIAccessibilityCustomAction(name: "Categoritza") { _ in
self.delegate?.categorizar(m); return true
},
UIAccessibilityCustomAction(name: "Comparteix") { _ in
self.delegate?.compartir(m); return true
}
]
// La icona és decorativa
iconoView.isAccessibilityElement = false
}
}Fixa't en importeAccesible: «−42,30 €» es llegeix millor com «menys quaranta-dos euros amb trenta cèntims» o almenys «−42,30 euros»; el símbol es pot llegir malament segons la veu. Per als botons d'icona, assigna sempre accessibilityLabel («Cerca», «Menú») o VoiceOver llegirà el nom del fitxer («ic_search_24»).
Anuncis i canvis de pantalla
Per notificar VoiceOver fes servir UIAccessibility.post(notification:argument:): .announcement amb un text per als missatges d'estat (4.1.3), .layoutChanged amb la vista que ha de rebre el focus quan canvia una part de la pantalla, i .screenChanged quan apareix una pantalla o un modal nou. A SwiftUI, AccessibilityNotification.Announcement("Desat").post() des d'iOS 17.
SwiftUI
SwiftUI deriva molta accessibilitat de la declaració (un Button amb text ja té nom i rol). Modificadors clau:
struct FilaMovimiento: View {
let m: Movimiento
var body: some View {
HStack {
Image(systemName: m.simbolo)
.accessibilityHidden(true) // decoratiu
VStack(alignment: .leading) {
Text(m.concepto).font(.body)
Text(m.fechaFormateada).font(.caption)
}
Spacer()
Text(m.importeFormateado)
.font(.body.monospacedDigit())
}
.accessibilityElement(children: .combine) // un sol element
.accessibilityLabel("\(m.concepto), \(m.importeAccesible), \(m.fechaLarga)")
.accessibilityAddTraits(.isButton)
.accessibilityAction(named: "Categoritza") { categorizar(m) }
.accessibilityAction(named: "Comparteix") { compartir(m) }
}
}
// Encapçalament de secció
Text("Moviments de març")
.font(.title2)
.accessibilityAddTraits(.isHeader)
// Imatge informativa
Image("grafico-gastos")
.accessibilityLabel("Despeses per categoria: alimentació 40 %, transport 25 %, oci 20 %, altres 15 %")Altres modificadors útils: .accessibilityValue per als estats («Activat»), .accessibilityAdjustableAction per als controls de tipus lliscant personalitzats, .accessibilitySortPriority per a l'ordre dins d'un grup, @AccessibilityFocusState per moure el focus de VoiceOver (per exemple, al primer error d'un formulari), .accessibilityInputLabels per a Voice Control, i .accessibilityRepresentation perquè una vista personalitzada es comporti com un control estàndard.
Error freqüent
Botons fets amb Image + .onTapGesture a SwiftUI o amb UIImageView + UITapGestureRecognizer a UIKit. VoiceOver els anuncia com a «imatge», sense el tret de botó, i de vegades ni tan sols són enfocables. Fes servir Button o UIButton: hereten el rol, el focus, l'activació amb doble toc i Voice Control sense esforç.
Dynamic Type
Dynamic Type és el sistema d'escalat del text d'iOS: l'usuari tria entre 12 mides (7 d'estàndard i 5 d'accessibilitat). L'aplicació el respecta si:
- A UIKit fa servir
UIFont.preferredFont(forTextStyle: .body)(oUIFontMetricsper a fonts personalitzades) i activaadjustsFontForContentSizeCategory = truea les etiquetes, els botons i els camps. - A SwiftUI fa servir els estils (
.font(.body),.headline…); s'escalen automàticament. Per a les mesures associades al text (icones, espaiats) fes servir@ScaledMetric. - Les etiquetes tenen
numberOfLines = 0i les restriccions permeten créixer; a SwiftUI evita.lineLimit(1)i les alçades fixes. - En mides d'accessibilitat grans, canvia la disposició:
@Environment(\.dynamicTypeSize)permet passar d'HStackaVStackquandynamicTypeSize.isAccessibilitySize. A UIKit, consultatraitCollection.preferredContentSizeCategory.isAccessibilityCategory. - Les barres de pestanyes i de navegació mostren versions ampliades en prémer llargament si es fan servir els components estàndard.
Prova amb la mida «AX5» i amb «Text en negreta» activat: és la prova de foc de l'1.4.4.
Voice Control i Full Keyboard Access
Voice Control (Configuració > Accessibilitat) permet controlar iOS amb la veu: «Toca Envia», «Mostra els números», «Mostra els noms». Funciona amb l'accessibilityLabel: si el botó d'icona no té etiqueta, l'usuari no el pot anomenar. Si l'etiqueta visible és llarga, afegeix accessibilityUserInputLabels = ["Envia", "Tramet"] per oferir alternatives curtes. Full Keyboard Access permet fer servir un teclat Bluetooth amb Tab; els controls estàndard ho admeten, les vistes personalitzades necessiten canBecomeFocused i la gestió d'UIFocusEnvironment. Switch Control recorre els elements accessibles en ordre, per la qual cosa un ordre i una agrupació ben fets també el beneficien.
Altres ajustos del sistema que l'aplicació ha de respectar
UIAccessibility.isReduceMotionEnabled/@Environment(\.accessibilityReduceMotion): desactiva les animacions no essencials.isReduceTransparencyEnabled,isDarkerSystemColorsEnabled(Augmentar el contrast),isBoldTextEnabled: fes servir els colors del sistema o defineix variants d'alt contrast al catàleg de colors.buttonShapesEnabled: mostra un subratllat o una vora als botons de només text si està activat.isVoiceOverRunning: útil per ajustar comportaments (per exemple, no fer avançar automàticament un carrusel), mai per oferir una «versió» diferent de l'aplicació.
Exemple
Formulari d'alta: en prémer «Continua» amb un camp buit, l'aplicació marca el camp en vermell. Per a VoiceOver s'hi afegeix: el missatge d'error com a text visible associat al camp (l'accessibilityLabel del camp inclou «Error: el NIF és obligatori» o s'enllaça amb accessibilityValue), un anunci «Hi ha 1 error al formulari» amb .announcement, i es mou el focus al primer camp amb error mitjançant @AccessibilityFocusState. Així es compleixen el 3.3.1, el 4.1.3 i el 2.4.3.
Resum
- VoiceOver llegeix l'etiqueta, el valor, els trets i la pista; el desenvolupador els defineix amb
accessibilityLabel,accessibilityValue,accessibilityTraitsiaccessibilityHint(o els seus modificadors de SwiftUI). - Agrupa les cel·les compostes en un element (
isAccessibilityElement/.accessibilityElement(children: .combine)) i exposa les accions amb custom actions. - Fes servir
Button/UIButtonper als botons, marca els encapçalaments amb.header, amaga el que és decoratiu i anuncia els canvis amb notificacions d'accessibilitat. - Dynamic Type: fonts preferides,
adjustsFontForContentSizeCategory, sense línies ni alçades fixes, disposició alternativa en les mides d'accessibilitat. - Voice Control depèn de les etiquetes; respecta Reduir el moviment, Augmentar el contrast i Text en negreta.
Portes 3 lliçons d'aquest curs: que no es perdin
Crea el teu compte i desa el que ja has llegit i segueix el curs amb tot:
- El test d'aquesta lliçó (3 preguntes) per comprovar el que has après
- Exercicis pràctics amb pàgines reals
- El teu progrés desat: continues on ho vas deixar, des de qualsevol dispositiu
- Constància del curs amb les hores i certificat del nivell 6 quan aprovis l'examen
Entres amb el teu compte de Google o només amb el teu correu electrònic. No cal contrasenya.
Matricula-m'hi · nivell 6 (119 €)