Bakh’iOS


Contexte

À lire

  • Prenez 5mn pour mieux comprendre la question de l’accessibilité numérique si ce n’est pas encore fait (site Bakhtech section Hello’ Accessibilité)
  • Prenez 5mn pour naviguer comme un aveugle avec le lecteur d’écran intégré aux appareils sous iOS/iPad OS VoiceOver
  • Ces 2 premiers points sont essentiels pour comprendre l’accessibilité numérique et constatés par soi même les impacts sur un utilisateur en situation de handicap. La prise en main de Voice Over vous sera aussi utile pour tester et valider vos corrections.

Recommandations

  • Les images porteuses d’information doivent avoir une alternative textuelle pertinente (en utilisant accessibilityLabel)
  • Les images décoratives doivent être ignorées par voice over. (Ce qui est le cas par défaut, il n y a donc rien à faire)
En savoir plus

Objets et propriétés concernés

Code


			//Donner une alternative textuelle à une image
			imageView.accessibilityLabel = "Un texte alternatif pertinent"
			
			

  • Ne jamais donner une information qu’avec la couleur
  • Utiliser des contrastes de couleurs qui respectent les seuils exigés
En savoir plus

Classes et propriétés concernés

  • Classes : UIView
  • Propriétés : color, background-color, tintColor

Cas particuliers – Notes techniques – Exceptions à la règle :

  • Non concerné par les critères de contraste de couleur : Logo, dénomination commerciale
  • Pour corriger les contrastes de couleur une alternative serait d’ajouter un bouton permettant d’inverser les couleurs ou de renforcer les contrastes sur l’ensemble de l’application en un “clic”.

Au delà des normes :

  • Les médias doivent avoir :
    • Une transcription textuelle adjacente ou accessible via un lien ou bouton adjacent ou des sous-titres synchronisés pour être accessibles aux personnes ayant un handicap auditif (si nécessaire)
    • Une transcription textuelle ou une audiodescription adjacente accessible via un lien ou bouton adjacent pour être accessibles aux personnes ayant un handicap visuel (si nécessaire)
  • Chaque son de plus de 3 secondes déclenché automatiquement doit être contrôlable par l’utilisateur (bouton stop, mute ou volume spécifique)
  • Les boutons de controles des medias doivent être compatible avec les technologies d’assistances

Classes et Propriétés concernés

  • Classes : AVPlayer, AVPlayerViewController, WKWebView

Cas particuliers – Notes techniques – Exceptions à la règle :

  • Le média est utilisé comme CAPTCHA ou test
  • Le média est décoratif (dans ce cas, vérifier qu’il soit bien ignoré par Voice Over)

  • Les composants customs doivent être totalement contrôlables (utilisables) par les technologies d’assistances. Les informations suivantes en particulier : nom, role, valeur, paramétrage et changements d’état du composant doivent être restitués au lecteur d’écran. À défaut une alternative pertinente et fonctionnelle avec ces outils doit être proposée.
  • Les messages de statut doivent être correctement restitués à Voice Over

Classes et propriétés concernés

Code


		//Nom du composant (exemple avec un onglet custom)
		tabView.accesibilityLabel = "Onglet 1 sur 2"
		//Role et état du composant
		tabView.accessibilityTraits = [.button,.selected]
		//Valeur de l'élément (exemple : slider)
		sliderView.accessibilityValue = "80%"
		
		//Donner plus d'infos sur l'utilisation/actions du composant
		sliderView.accessibilityHint
		
		//Déclencher une vocalisation en cas de changement de contenu (permet également de mettre le focus voice over sur l'élément en question)
		UIAccessibility.post(notification: .layoutChanged, argument: tabPanelLabel)
		
		//Faire lire un texte par Voice Over
		let message = NSAttributedString(
        string: "Texte à vocaliser.",
        attributes: [.accessibilitySpeechQueueAnnouncement: true]) /*indique à VoiceOver d'attendre la fin des annonces en court d'abord*/
      	UIAccessibility.post(notification: .announcement,
                           argument: message)
		
		
		//Exemple : rendre un slider custom accessible à VoiceOver
		class VolumeSlider : UIView {
		
			var value = "50%"
			override func layoutSubviews() {
				super.layoutSubviews()
				...
				accessibilityLabel = "Volume"
				accessibilityHint = "Ajuste la valeur du volume"
				accessibilityTraits = .adjustable //Indique à voice over que c'est un élément dont la valeur est ajustable par l'utilisateur. Avec voice over actif, l'utilisateur peut alors glisser le doigt vers le haut pour incrémenter le slider ou vers le bas pour le décrémenter.
				accessibilityValue = value
				
			}
			
			 override func accessibilityIncrement() { 
			 	//fonction appelée lorsque l'on incrémente le slider via VoiceOver (en glissant le doigt vers le haut). Il faut executer ici executer la même action que pour une incrémentation normale puis mettre à jour la valeur annoncé par voice over
				...
				accessibilityValue = value
			 }
			 
			  override func accessibilityDecrement() { 
			 	//Idem mais pour la décrémentation
				...
				accessibilityValue = value
			 }
		
		
		}
		
		//Utiliser les actions personnalisés pour effectuer des actions demandant des gestes complexes de manière simples avec VoiceOver (par exemple un glisser pour archiver un mail)
		class ViewController: UIViewController {
			override func viewDidLoad() {
				super.viewDidLoad()
				...
				let archive = UIAccessibilityCustomAction(
				  name: "Archiver le mail",
				  target: self,
				  selector: #selector(archiveAction))
				accessibilityCustomActions = [archive] 
				 
			}
				
			@objc func archiveMail() -> Bool {
				//effectuer l'action puis retourner true ou alors retourner false si l'action n'est pas effectuable (Par exemple dans le cas d'un bouton page suivante, si on est déjà sur la dernière page)
			 }
		}
		
		
			

	

Cas particuliers – Notes techniques – Exceptions à la règle :

  • Il existe une gestion de cas particulier lorsque la fonctionnalité dépend de l’utilisation d’un gestionnaire d’événement sans équivalent universel ; par exemple, une application de dessin à main levée ne pourra pas être rendue contrôlable via les technologies d’assistances. Dans ces situations, le critère est non applicable.

Au delà des normes :

  • Il est également possible d’utiliser les actions personnalisées pour tout simplement créer un raccourci. Par exemple, si on a un bouton en bas à droite de l’écran, on peut créer une action personnalisée ayant la même action que ce bouton pour éviter que l’utilisateur ait à parcourir toute la page afin de l’atteindre
  • S’il n y a aucun correspondant au composant custom (par exemple pour les checkbox qui n’existent pas nativement sur iOS), indiquer le role du composant dans le accessibilityLabel

  • Chaque écran doit avoir un titre qui lui est propre ou qui permet de se repérer dans la navigation
  • Indiquer au lecteur d’écran les changements de langue dans une vue

Html, CSS et JS concernés

Code


				//Indiquer qu'un label contient du texte en anglais
				label.accessibilityLanguage = "en"
			

  • Utiliser accessibilityTraits pour indiquer qu’un texte est un titre
  • Indiquer la présence de citation
En savoir plus

Code


				//Indiquer qu'un texte est un titre
				titleLabel.accessibilityTraits = .header
				
				//Indiquer qu'un texte est une citation
				label.accessibilityLabel = "Citation : " + label.text 
				
				

  • Utiliser le type dynamique pour les textes afin que l’utilisateur puisse ajuster leur taille via les paramètres d’accessibilité
  • Le contenu visible doit le rester quelque soit la taille de texte choisi par l’utilisateur
  • La taille des glyphes apportant une information doivent également s’adapter à la taille de texte choisi
  • Chaque lien dont la nature n’est pas évidente doit être visible par rapport à son environnement
  • Les contenus cachés doivent aussi être ignorés par les technologies d’assistance
  • Ne donner aucune information uniquement avec la forme, la taille ou la position
  • Le contenu doit être présenté sans avoir besoin d’un scrolling vertical si le sens de lecture est horizontal d’un scrolling horizontal si le sens de lecture est vertical

Classes et propriétés concernés

Code


					//Masquer un élément à voice over
					view.isAccessibilityElement = false
					
					//Utiliser une police dynamique pour du texte (ici le corps du texte)
					label.font = UIFont.preferredFont(forTextStyle: .body)
					//Gérer le changement de taille de texte en temps réel (Au lieu de devoir rédémarrer l'appli pour voir les changements)
					label.adjustsFontForContentSizeCategory = true
		
					//Faire d'une police normale une police dynamique (Par exemple si la taille ou la police par défaut ne nous convient pas)
					let labelFont =  UIFont.boldSystemFont(ofSize: 30.0)
            		label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: labelFont)
					label.adjustsFontForContentSizeCategory = true
					
					//Adapter la taille d'une image de manière automatique
					imageView.adjustsImageSizeForAccessibilityContentSizeCategory = true
					
					//Adapter la taille d'un élément graphique manuellement
					func adjustSize(){
						let adjustedHeight = UIFontMetrics(forTextStyle: .body).scaledValue(for: 50.0)
        				buttonHeight.constant = adjustedHeight
						...
					}
					override func viewDidLoad() { //au lancement
						...
						adjustSize();
    				}
					 override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
					 	//lorsque l'utilisateur change la taille du texte dans les paramètres
					 	adjustSize();
					}
					
				

Cas particuliers – Notes techniques – Exceptions à la règle :

  • Dans le cas des textes en image et des sous-titres de vidéo le critère sur la lisibilité du contenu après zoom du texte n’est pas applicable
  • Le critère sur l’absence scrolling n’est pas applicables sur les images, graphiques, vidéos, jeux, présentations, tableaux de données, interface où il est nécessaire d’avoir un ascenseur horizontal lors de la manipulation de l’interface et navigateurs mobiles

Au delà des normes :

  • Chaque champ du formulaire doit être associé à une étiquette pertinente, les 2 doivent être accolés (étiquette au dessus ou à gauche du champ)
  • Si le champ est répété sur une page ou dans un ensemble de pages, cela doit être fait de manière cohérente
  • Indiquer la présence de champ obligatoire ainsi que le type de données attendu si nécéssaire
  • Utiliser le type de clavier correspondant au type de données à entrer pour faciliter la saisie
  • Dans les cas suivants proposer à l’utilisateur de confirmer son action : modification ou suppression de données, réponse à un test ou à un examen, validation aux conséquences financières ou juridiques

Classes et Propriétés concernés

Code


				//Donner une étiquette accessible à un champ (En plus de l'étiquette texte accolée)
				textField.accessibilityLabel = "Nom du champ"
				
				//Donner plus d'informations sur un champ
				textField.accessibilityHint = "Champ Obligatoire. Nombre entre 1 et 100"
				
				
				//Changer le type de clavier pour un champ (ici clavier numérique)
				textField.keyboardType = .numberPad
				
				//Faciliter le remplissage automatique
				textField.textContentType = .emailAddress
				
				
			

Au delà des normes :

  • L’ordre de parcours avec Voice Over doit être cohérent

Classes et propriétés concernés

Code


				//Modifier l'ordre de parcours voice over si celui par défaut n'est pas cohérent
				
				override func viewDidLoad() {
					...
					 accessibilityElements = [
					  element1,
					  element2,
					  element3,
					  element4,
					  element5,
					  ].compactMap { $0 }

				}
				
				
				
			

  • L’utilisateur doit avoir le contrôle de chaque limite de temps qui modifie le contenu
  • L’ouverture d’une nouvelle fenêtre ne doit pas être déclenché sans action de l’utilisateur
  • Les documents bureautiques en téléchargement doivent aussi être accessibles tout en apportant la même information
  • Prévoir une alternative pertinente pour les contenus cryptiques (art ASCII, émoticon, syntaxe cryptique)
  • Utiliser correctement les changements brusques de luminosité ou les effets flash
  • Chaque contenu en mouvement ou clignotant doit être contrôlable par l’utilisateur
  • Le contenu doit être consultable quelque soit l’orientation de l’écran
  • Les fonctionnalités utilisables ou disponibles au moyen d’un geste complexe doivent aussi être disponibles à l’aide d’un geste simple
  • les actions déclenchées au moyen d’un dispositif de pointage sur un point unique de l’écran peuvent-elles faire l’objet d’une annulation
  • les fonctionnalités qui impliquent un mouvement de l’appareil ou vers l’appareil doivent pouvoir être satisfaites de manière alternative

Cas particuliers – Notes techniques – Exceptions à la règle :

  • Le critère sur la limite de temps n’est pas applicable lorsque celle ci est essentielle, notamment lorsqu’elle ne pourrait pas être supprimée sans changer fondamentalement le contenu ou les fonctionnalités liées au contenu
  • Le critère sur l’orientation de l’écran n’est pas applicable si celle ci est essentielle à l’utilisation de l’interface. (ex : jeu)
  • Idem pour le critère sur les mouvements de l’appareil. (ex : podomètre)
  • Le critère sur les gestes complexes ne s’appliquent pas aux gestes requis par l’agent utilisateur ou le système d’exploitation
  • Idem pour le critère sur les actions déclenchées au moyen d’un dispositif de pointage. De plus les fonctionnalités qui imitent un clavier ne sont pas concernés