├── LICENSE.md ├── README.md ├── android.md └── ios.md /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Licence 2 | Ces documents sont des documents de l'État français placés sous licence ouverte 1.0 ou ultérieure. Vous trouverez des précisions sur cette licence sur le [blog de la mission etalab](http://www.etalab.gouv.fr/licence-ouverte-open-licence). 3 | 4 | Vous êtes libres de : 5 | * Reproduire, copier, publier et transmettre ces informations ; 6 | * Diffuser et redistribuer ces informations ; 7 | * Adapter, modifier, extraire et transformer ces information, notamment pour créer des informations dérivées ; 8 | * Exploiter ces informations à titre commercial, par exemple en la combinant avec d'autres informations, ou enl'incluant dans votre propre produit ou application. 9 | 10 | Ces libertés s'appliquent sous réserve de mentionner la paternité de l'information d'origine : sa source et la date de sa dernière mise à jour. Le réutilisateur peut notamment s'acquitter de cette condition en indiquant un ou des liens hypertextes (URL) renvoyant vers le présent dépôt et assurant une mention effective de sa paternité. 11 | 12 | Cette mention de paternité ne doit ni conférer un caractère officiel à la réutilisation de ces informations, ni suggérer une quelconque reconnaissance ou caution par le producteur de l'information, ou par toute autre entité publique, du réutilisateur ou de sa réutilisation. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guide de développement d'applications mobiles accessibles avec les API Android et iOS 2 | 3 | Ce guide s'adresse aux développeurs d'applications mobiles qui souhaitent 4 | connaître les techniques à utiliser pour créer des applications accessibles 5 | avec les API fournies par les systèmes Android et iOS. Pour chaque 6 | environnement, l'accessibilité des widgets 7 | le plus courants a été testés avec les lecteurs d'écran. 8 | 9 | Deux documents distincts sont proposés pour : 10 | 11 | * [Android](./android.md) 12 | * [iOS](./ios.md) 13 | 14 | Le guide est [disponible également en anglais](https://github.com/DISIC/guide-mobile_app_dev_natif/tree/english). 15 | 16 | ## Guides connexes 17 | 18 | Les guides suivants peuvent être consultés en complément : 19 | 20 | * [Guide d'audit d'applications mobiles](https://github.com/DISIC/guide-mobile_app_audit) 21 | * [Guide de conception d'applications mobiles accessibles](https://github.com/DISIC/guide-mobile_app_conception) 22 | * [Guide de développement d'applications mobiles accessibles avec Ionic et OnsenUI](https://github.com/DISIC/guide-mobile_app_dev_hybride) 23 | 24 | ## Licence 25 | Ce document est la propriété du Secrétariat général à la modernisation de l'action publique français (SGMAP). Il est placé sous la [licence ouverte 1.0 ou ultérieure](http://www.etalab.gouv.fr/licence-ouverte-open-licence), équivalente à une licence Creative Commons BY. Pour indiquer la paternité, ajouter un lien vers la version originale du document disponible sur le [compte Github de la DInSIC](https://github.com/DISIC). 26 | 27 | -------------------------------------------------------------------------------- /android.md: -------------------------------------------------------------------------------- 1 | # Guide de développement d'applications accessibles mobiles avec l'API Android 2 | 3 | ## Sommaire 4 | 5 | * [Sommaire](#sommaire) 6 | * [À qui s'adresse ce guide ?](#%C3%A0-qui-sadresse-ce-guide ) 7 | * [Généralités sur Android et l'accessibilité](#g%C3%A9n%C3%A9ralit%C3%A9s-sur-android-et-laccessibilit%C3%A9) 8 | * [Utliser les éléments de base fournis par l'API Android](#utliser-les-%C3%A9l%C3%A9ments-de-base-fournis-par-lapi-android) 9 | * [Widgets compatibles](#widgets-compatibles) 10 | * [Widgets incompatibles](#widgets-incompatibles) 11 | * [Tester l'accessibilité](#tester-laccessibilit%C3%A9) 12 | * [Décrire les éléments d'interface](#d%C3%A9crire-les-%C3%A9l%C3%A9ments-dinterface) 13 | * [Exemples de création statique](#exemples-de-cr%C3%A9ation-statique) 14 | * [ImageView et ImageButton](#imageview-et-imagebutton) 15 | * [CheckBox](#checkbox) 16 | * [EditText](#edittext) 17 | * [Mise à jour dynamique de la description des contenus](#mise-%C3%A0-jour-dynamique-de-la-description-des-contenus) 18 | * [Permettre la modification de la taille des caractères](#permettre-la-modification-de-la-taille-des-caract%C3%A8res) 19 | * [Gérer le focus](#g%C3%A9rer-le-focus) 20 | * [Activer la prise de focus](#activer-la-prise-de-focus) 21 | * [Contrôler l'ordre de lecture](#contr%C3%B4ler-lordre-de-lecture) 22 | * [Afficher la prise de focus](#afficher-la-prise-de-focus) 23 | * [Masquer les éléments décoratifs](#masquer-les-%C3%A9l%C3%A9ments-d%C3%A9coratifs) 24 | * [Forcer la restitution des éléments importants non focusables](#forcer-la-restitution-des-%C3%A9l%C3%A9ments-importants-non-focusables) 25 | * [Regrouper des éléments](#regrouper-des-%C3%A9l%C3%A9ments) 26 | * [Gérer des événements liés à l'accessibilité](#g%C3%A9rer-des-%C3%A9v%C3%A9nements-li%C3%A9s-%C3%A0-laccessibilit%C3%A9) 27 | * [AccessibilityEvent](#accessibilityevent) 28 | * [Cheminement des événements](#cheminement-des-%C3%A9v%C3%A9nements) 29 | * [Récupérer plus d'informations sur le contexte avec AccessibilityNodeInfo](#r%C3%A9cup%C3%A9rer-plus-dinformations-sur-le-contexte-avec-accessibilitynodeinfo) 30 | * [Créer des vues personnalisées accessibles](#cr%C3%A9er-des-vues-personnalis%C3%A9es-accessibles) 31 | * [Gérer la navigation par contrôleurs directionnels](#g%C3%A9rer-la-navigation-par-contr%C3%B4leurs-directionnels) 32 | * [Implémenter les méthodes pour l'accessibilité](#impl%C3%A9menter-les-m%C3%A9thodes-pour-laccessibilit%C3%A9) 33 | * [onInitializeAccessibilityEvent()](#oninitializeaccessibilityevent) 34 | * [onInitializeAccessibilityNodeInfo()](#oninitializeaccessibilitynodeinfo) 35 | * [onPopulateAccessibilityEvent()](#onpopulateaccessibilityevent) 36 | * [Gérer le cas des gestes personnalisés](#g%C3%A9rer-le-cas-des-gestes-personnalis%C3%A9s) 37 | * [Pour aller plus loin](#pour-aller-plus-loin) 38 | * [Guides connexes](#guides-connexes) 39 | * [Ressources externes et références](#ressources-externes-et-r%C3%A9f%C3%A9rences) 40 | * [Licence](#licence) 41 | 42 | ## À qui s'adresse ce guide ? 43 | 44 | Ce guide présente des éléments de l'API d'Android utiles pour développer des applications accessibles aux personnes en situation de handicap. Il s'adresse : 45 | 46 | * Aux développeurs 47 | * Aux concepteurs en charge de la rédaction de spécifications techniques 48 | * Aux chefs de projets 49 | 50 | Pré-requis : 51 | 52 | * Maîtriser les langages Java et XML 53 | * Maîtriser les principes de programmation sous Android 54 | * Connaître les classes de base de l'environnement Android (notamment "activity" et "service") 55 | * Maîtriser la gestion d'événements 56 | * Maîtriser les concepts utilisés pour gérer les interfaces utilisateurs sous Android (vues, layouts, etc.) et les principaux éléments de l'API (TextView, CheckBox, RadioButton, etc.) 57 | 58 | 59 | ## Généralités sur Android et l'accessibilité 60 | 61 | L'API Android met à disposition des développeurs des fonctionnalités permettant de créer des applications accessibles. Ces fonctionnalités sont présentes dans les classes qui permettent de créer les éléments de base d'une interface utilisateur ainsi que dans des classes dédiées à l'accessibilité. 62 | 63 | Les applications Android peuvent être utilisées par des personnes en situation de handicap (visuel, moteur, etc.), grâce à l'activation de services dédiés à l'accessibilité sur l'appareil utilisé pour exécuter l'application et parfois grâce à l'utilisation de périphériques externes (claviers, commutateurs, plages braille, etc.). Les services d'accessibilité peuvent être activés en se rendant dans les paramètres de l'appareil, dans l'onglet "Accessibilité". 64 | 65 | Google développe plusieurs services : 66 | 67 | * Le lecteur d'écran TalkBack, qui vocalise la navigation à l'intérieur des applications. Il propose plusieurs fonctionnalités : 68 | * Exploration tactile : l'utilisateur explore le contenu de l'application en parcourant l'écran avec les doigts. TalkBack pilote un logiciel de synthèse vocale qui prononce les informations relatives aux éléments explorés positionnés sous les doigts de l'utilisateur. Dans ce mode, TalBack redéfinit les gestes "standards" : l'activation d'un élément se fait par exemple par un "double tap". 69 | * Navigation gestuelle simplifiée : TalkBack redéfinit des gestes permettant à l'utilisateur de naviguer facilement entre les différents éléments de l'interface. 70 | * Retour haptique : lorsque des éléments d'interface sont activés (entrée dans une zone de saisie par exemple), TalkBack le signale à l'utilisateur par des vibrations. 71 | * BrailleBack, qui permet aux utilisateur possédant des appareils de lecture braille éphémère de les connecter via USB ou Bluetooth à leur appareil Android pour lire et interagir avec le contenu des applications. 72 | 73 | De même que certains constructeurs proposent des versions modifiées du système Android, il existe des versions modifiées des services d'accessibilité : c'est par exemple le cas des certains téléphones de la marque Samsung qui sont livrés avec le service Galaxy TalkBack, une version du lecteur d'écran basée sur celui développé par Google. 74 | 75 | La simple activation de services d'accessibilité ne suffit pas à rendre une application pleinement accessible : le développeur doit mettre en oeuvre des techniques particulières lors du codage de l'application pour rendre son contenu exploitable par les services d'accessibilité. 76 | 77 | Lorsque l'interface utilisateur d'une application est constituée d'éléments de base fournis par le framework Android, la mise en oeuvre de l'accessibilité est relativement simple. La démarche est la suivante : 78 | 79 | 1. Décrire les éléments d'interface pour que leur contenu soit restitué par les services d'accessibilité 80 | 2. Faire en sorte que tous les éléments qui appellent une interaction puissent être atteints par un contrôleur directionnel (trackball, D-pad, etc.) 81 | 3. Faire en sorte que les messages audio soient accompagnés par des notifications visuelles et/ou haptiques, pour être perçues par les personnes sourdes ou malentendantes 82 | 83 | Afin de développer des interfaces plus complexes et personnalisées qui nécessitent d'étendre la classe View, il sera nécessaire de s'assurer que ces éléments sont compatibles avec les services d'accessibilité en redéfinissant certaines méthodes, notamment pour gérer correctement les événements liés à l'accessibilité. 84 | 85 | ## Utliser les éléments de base fournis par l'API Android 86 | 87 | L'API d'Android fournit des éléments de base (widgets, 88 | views) qui implémentent correctement l'accessibilité dans la plupart 89 | des cas. Il convient en premier lieu de privilégier l'utilisation de ces 90 | éléments et de n'en définir de nouveaux qu'en cas de nécessité. 91 | 92 | Nous listons ci-dessous des éléments compatibles et non compatibles avec les 93 | services d'accessibilité. Ils ont été testés avec les versions d'Android 4.3, 94 | 4.4 et 5.1. 95 | 96 | ### Widgets compatibles 97 | 98 | Les widgets suivants ont été testés et peuvent être utilisés pour créer des interfaces compatibles avec les services d'accessibilité (en veillant à décrire les contenus et à gérer correctement le focus comme indiqué dans les sections précédentes). 99 | 100 | * EditText 101 | * CheckBox 102 | * ToggleButton 103 | * Switch 104 | * RadiGroup et RadioButton 105 | * Chronometer 106 | * TectClock 107 | * AutoCompleteTextView 108 | * Button 109 | * ProgressBar 110 | * RatingBar 111 | * SeekBar 112 | * NumberPicker 113 | * TextSwitcher 114 | 115 | 116 | Layouts : 117 | 118 | * LinearLayout 119 | * TableLayout 120 | 121 | Vues : 122 | 123 | * TextView 124 | * ImageView 125 | * ListView 126 | * SearchView 127 | * WebView 128 | 129 | ### Widgets incompatibles 130 | En revanche, les widgets suivants ne sont pas compatibles avec les services d'accessibilité. Ils devront être adaptés au cas par cas : 131 | 132 | * TabHost : le contenu des onglets n'est pas restitué correctement par les services d'accessibilité 133 | * DatePicker 134 | * TimePicker 135 | * Spinner 136 | * ToolBar 137 | 138 | Vues : 139 | 140 | * CalendarView 141 | * ExpandableListView 142 | * StackView 143 | 144 | ## Tester l'accessibilité 145 | 146 | Dans l'API Android, les fonctionnalités relatives à l'accessibilité sont "stabilisées" depuis la version 4.0 (il n'y pas de changement majeur dans l'API). Cependant, les services d'accessibilité (TalkBack notamment) évoluent rapidement et changent parfois l'interprétation qu'ils font de certains éléments d'interface ou de certains événements liés à l'accessibilité. Par exemple, la version 4.3.1 de TalkBack parue en octobre 2015 est en mesure de restituer les changements de valeurs des "sliders", ce qui n'était pas le cas dans les versions précédents (on devait alors utiliser l'attribut `contentDescription` pour en restituer le contenu). L'interprétation par les services d'accessibilité des widgets courants est relativement stable : les tests réalisés n'ont pas permis de mettre en évidence des régressions. En revanche, l'interprétation de vues personnalisées peut varier, mais ces variations sont extrêmement complexes à documenter. Par ailleurs, certains constructeurs de matériel peuvent ajouter des surcouches au système Android, ce qui a parfois un impact sur l'accessibilité. Il est ainsi nécessaire de tester l'accessibilité d'une application Android afin de vérifier que les recommandations présentées ci-dessous sont bien prises en compte par les services d'accessibilité (voir le [Guide d'audit d'applications mobiles](https://github.com/DISIC/guide-mobile_app_audit)). 147 | 148 | ## Décrire les éléments d'interface 149 | Un grand nombre d'éléments d'une interface utilisateur fournissent des informations sur leur usage ou sur leur signification grâce à des indications visuelles. Par exemple, une application de prise de note utilise un élément `ImageButton` contenant l'image d'un signe "plus" pour indiquer que l'utilisateur peut ajouter une nouvelle note. Un composant `EditText` peut disposer d'une étiquette qui permet de préciser l'information que l'utilisateur doit saisir. Un utilisateur aveugle ne peut pas percevoir ces indications visuelles. Il est donc nécessaire d'employer un moyen pour ajouter une alternative à ces indications. Pour cela il faut utiliser l'attribut `android:contentDescription` dans la description XML d'un élément d'interface ou la méthode `setContentDescription` dans le code Java. Le texte qui se trouve dans cette description ne sera pas affiché sur l'écran mais sera uniquement traité par les services d'accessibilité lorsque l'utilisateur les aura activés. Avec TalkBack, le texte sera prononcé par le synthétiseur vocal ; avec BrailleBack, le texte apparaîtra à proximité de l'élément sur l'afficheur braille. 150 | 151 | ### Exemples de création statique 152 | 153 | #### ImageView et ImageButton 154 | `ImageView` permet d'afficher une image à l'écran. `ImageButton` est une sous-classe d'`ImageView` qui lui ajoute les comportements d'un bouton "standard". Ces classes possèdent un attribut `android:src` qui désigne une ressource graphique correspondant à l'image qui sera affichée et utilisée pour représenter le bouton dans le cas d'`ImageButton`. Cette ressource graphique ne pouvant pas être interprétée par les services d'accessibilité, il convient d'utiliser l'attribut `android:contentDescription` pour fournir une alternative textuelle à cette image. 155 | 156 | Exemple avec `ImageButton` : 157 | ```xml 158 | 164 | ``` 165 | 166 | Comportement attendu avec TalkBack : annonce du contenu de l'attribut `android:contentDescription` précédé du mot "bouton" lors de la prise de focus ou du survol de l'image 167 | 168 | 169 | #### CheckBox 170 | `CheckBox` permet d'implémenter une case à cocher. 171 | 172 | ```xml 173 | 178 | ``` 179 | 180 | L'attribut `android:text` contient le texte affiché à l'écran. Il peut être pertinent d'ajouter des éléments de contexte dans l'attribut `android:contentDescription` pour fournir à l'utilisateur des précisions sur l'action qui sera réalisée lors de l'activation ou la désactivation de la case. Par exemple, une vue peut présenter plusieurs cases à cocher disposées en regard d'une liste de produits dont l'utilisateur doit indiquer si oui ou non il souhaite recommander un/des élément(s). Si seulement l'attribut "android:text" est renseigné avec le texte "Recommander", l'utilisateur ne disposera pas de suffisamment d'informations pour savoir quelle action réalisera le fait de cocher la case. Ajouter "Recommander des pivoines" dans l'attribut `android:contentDescription` lui permettra de connaître plus précisément l'action réalisée lorsqu'il utilise un service d'accessibilité. 181 | 182 | Comportement attendu avec TalkBack : prononciation de l'état de la case, suivi de "Case à cocher" puis du contenu de la description. 183 | 184 | #### EditText 185 | `EditText` permet de créer une zone de saisie de texte. Pour `EditText`, contrairement à la plupart des autres widgets disponibles dans le framework Android, l'attribut `android:contentDescription` ne peut pas être utilisé pour véhiculer une information décrivant l'élément (le contenu de l'attribut ne sera pas restitué par les services d'accessibilité). 186 | 187 | L'utilisation de `android:hint` permet à l'utilisateur de comprendre la nature de l'information attendue : le contenu de `android:hint` est une aide à la saisie affichée lorsque la zone de saisie est vide, et lorsque la zone est complétée, le lecteur d'écran restitue le texte saisi au lieu de l'attribut. 188 | ```xml 189 | 193 | ``` 194 | 195 | Cependant, le contenu de `android:hint` disparaît dès que l'utilisateur saisit un caractère et ne peut plus être restitué, même si la zone est vidée : cela créé des difficultés notamment dans lorsque plusieurs champs de saisie sont proposés dans une même vue. Le meilleur moyen d'assurer l'accessibilité d'une zone de saisie est d''y associer une étiquette visible en utilisant l'attribut `android:labelFor`. 196 | 197 | En XML : 198 | ```xml 199 | 202 | 203 | 204 | ``` 205 | 206 | En Java, les fonctions `setLabelFor(View label)` ou `setLabeledBy(View label)` peuvent être utilisées. 207 | 208 | Comportement attendu avec TalkBack : 209 | 210 | * Le contenu de `android:hint` est restitué uniquement lorsqu'aucun texte n'a été saisi ; lorsqu'un texte a été saisi, celui-ci est restitué par TalkBack ; lorsqu'un texte a été saisi puis supprimé, TalkBack indique seulement la présence de la zone de saisie sans restituer le contenu de `android:hint`. 211 | * Le contenu de `android:labelFor` est restitué quel que soit le contexte. 212 | 213 | ### Mise à jour dynamique de la description des contenus 214 | Lorsque le contenu d'un élément évolue au cours du temps, il est nécessaire de veiller à mettre à jour sa description, notamment lorsque l'état de l'élément ne peut être interprété par un autre moyen que cette description. Pour cela, il faut utiliser la méthode `setContentDescription` dans le code Java de l'application. La mise à jour dynamique de la description est par exemple utile dans les cas suivants, lors de la création de widgets personnalisés : 215 | 216 | * Pour sélectionner une couleur dans une palette : les couleurs doivent être identifiées par une description textuelle et la description indiquant la couleur sélectionnée doit être mise à jour afin que l'utilisateur en ait connaissance lorsqu'il reprendra le focus sur le widget 217 | * Pour sélectionner une date : la description peut être utilisée pour rappeler l'intégralité de la date sélectionnée après la mise à jour d'un des éléments (jour, mois, année) par l'utilisateur 218 | * Pour indiquer le changement de fonction d'un bouton image lorsque sa signification est véhiculée par la forme ou la couleur : un bouton "Lecture" devenant "Pause" par exemple 219 | 220 | Une méthode pour mettre à jour la description est d'utiliser des listeners pour surveiller le changement d'état d'un widget afin de prendre les mesures nécessaires au moment approprié. 221 | 222 | L'exemple ci-dessous met en oeuvre une `SeekBar`, un widget qui permet de saisir une valeur prise dans un intervalle déterminé. TalkBack, dans ses version inférieures à 4.3.1, ne surveillait pas le changement d'état de la `SeekBar` et n'était donc pas en mesure d'informer l'utilisateur des changements de valeurs. Ce comportement est identique avec BrailleBack. Le code proposé surveille la modification de la `SeekBar` et met à jour dynamiquement la description fournie aux services d'accessibilité lorsque la valeur change. Ainsi, la valeur sera indiquée à l'utilisateur après chaque changement, ce qui n'aurait pas été le cas par défaut. 223 | NB : ce code est donné à titre d'exemple  avec TalkBack 4.3.1, son utilisation provoque une double restitution lors du changement de la valeur de la `SeekBar`. Il doit donc être considéré comme une illustration du principe de mise à jour dynamique de la description, mais son utilisation n'est pas nécessairement pertinente dans tous les contextes. 224 | 225 | Description XML : 226 | ```xml 227 | 231 | 232 | 236 | 237 | ``` 238 | 239 | Code Java : 240 | ```java 241 | package com.braillenet.android_accessibility; 242 | 243 | import android.app.Activity; 244 | import android.os.Bundle; 245 | import android.widget.SeekBar; 246 | 247 | public class ProgressBarActivity extends Activity { 248 | 249 | private SeekBar mReadingSpeed; 250 | String name; 251 | 252 | @Override 253 | protected void onCreate(Bundle savedInstanceState) { 254 | super.onCreate(savedInstanceState); 255 | setContentView(R.layout.progressbar); 256 | mReadingSpeed = (SeekBar)findViewById(R.id.reading_speed); 257 | name = "Vitesse de lecture"; 258 | mReadingSpeed.setOnSeekBarChangeListener(new SeekBarListener(name)); 259 | mReadingSpeed.setContentDescription(name); 260 |  } 261 | 262 | private class SeekBarListener implements SeekBar.OnSeekBarChangeListener { 263 | private String mName; 264 | public SeekBarListener(String name) { 265 | mName = name; 266 | } 267 | public void onProgressChanged(SeekBar seekBar, int progress, 268 | boolean fromUser) { 269 | int percent = 100 * seekBar.getProgress() / seekBar.getMax(); 270 | seekBar.setContentDescription(mName + " " + percent + "%"); 271 | } 272 | public void onStartTrackingTouch(SeekBar seekBar) { 273 | } 274 | public void onStopTrackingTouch(SeekBar seekBar) { 275 | seekBar.setContentDescription(mName); 276 | } 277 | } 278 | } 279 | ``` 280 | 281 | Comportement attendu avec TalkBack : lorsque la valeur est modifiée (on peut utiliser les bouton "+" et "-" du volume pour cela), TalkBack annonce le message "Vitesse de lecture x%". 282 | 283 | ## Permettre la modification de la taille des caractères 284 | 285 | Android propose à l'utilisateur de paramétrer la taille des caractères (menu Paramètres, Accessibilité puis Taille des caractères : Petit, Normal ou Grand). Pour qu'une application réponde correctement à ce paramétrage, il est nécessaire d'utiliser l'unité "sp" (scale-independent pixel) lors de la définition des tailles de texte. 286 | Exemple : `` 287 | 288 | Il est possible de connaître le facteur d'ajustement sélectionné par l'utilisateur appliqué à la taille des caractères, ce qui permet d'adapter le comportement de l'application en conséquence (modifier le nombre de colonnes d'un tableau par exemple). Pour cela, il faut accéder à la valeur `Settings.System.FONT_SCALE` de la table `Settings.System.CONTENT_URI`. (1.0f est la valeur correspondant à la taille "normale"). 289 | 290 | Exemple : 291 | ```java 292 | ContentResolver r = getContentResolver(); 293 | Cursor c = r.query(Settings.System.CONTENT_URI, 294 | new String[] { Settings.System.VALUE }, 295 | Settings.System.NAME + "= ?", 296 | new String[] { Settings.System.FONT_SCALE }, null); 297 | float fs = c.getFloat(0); 298 | if(fs ...) { 299 | .. 300 | } else { 301 | ... 302 | } 303 | ``` 304 | 305 | ## Gérer le focus 306 | 307 | Il est nécessaire de permettre aux utilisateurs de naviguer entre les différents éléments de l'interface grâce à un contrôleur directionnel. Un tel contrôleur peut être matériel (trackball, D-Pad, flèches d'un clavier, etc.) ou virtuel comme le clavier fourni par une application ou le mode de navigation gestuelle disponible depuis Android 4.1. La navigation grâce aux contrôleurs directionnels est largement utilisée, notamment par les personnes en situation de handicap moteur. 308 | 309 | Pour s'assurer qu'une telle navigation fonctionne, il est nécessaire de vérifier que tous les éléments d'une application soient atteignables sans utiliser les interactions tactiles. Il est par ailleurs nécessaire de vérifier que cliquer sur le bouton central (ou le bouton OK) d'un contrôleur directionnel a le même effet que de toucher un élément sur l'écran possédant le focus. 310 | 311 | ### Activer la prise de focus 312 | 313 | Un élément d'une interface utilisateur est atteignable en utilisant un contrôleur directionnel lorsque l'attribut `android:focusable` vaut "true" : cela permet à l'utilisateur de prendre le focus sur cet élément et d'interagir avec le contenu. Les éléments de base fournis par le framework Android sont focusables par défaut. Le fait qu'un élément soit porteur ou non du focus est signalé par une modification de son apparence. 314 | L'API d'Android fournit plusieurs méthodes permettant de contrôler qu'un élément est bien focusable, ainsi que de donner le focus à un élément : 315 | 316 | * `setFocusable()` : indique que le focus pourra être pris sur l'élément 317 | * `isFocusable()` : indique si un élément peut recevoir le focus 318 | * `requestFocus()` : permet de donner le focus à un élément 319 | 320 | Si une vue ne peut pas recevoir le focus par défaut, il est possible d'utiliser l'attribut `android:focusable` avec la valeur "true" ou d'utiliser la méthode `setFocusable()` pour changer ce comportement. 321 | 322 | ### Contrôler l'ordre de lecture 323 | 324 | Lorsqu'un utilisateur navigue dans une application grâce à un contrôleur directionnel, le focus passe d'un élément de l'interface à l'autre selon un ordre déterminé par un algorithme qui cherche l'élément le plus proche dans la direction souhaitée. Dans certains cas, l'ordre calculé par cet algorithme peut ne pas être cohérent avec la logique de l'application. Pour contourner ce problème, il est possible de définir des relations entre les différents éléments. Les attributs `android:nextFocusDown` (respectivement `android:nextFocusUp`, `android:nextFocusLeft` et `android:nextFocusRight`) permettent de définir le prochain élément qui recevra le focus lorsque l'utilisateur se déplacera vers le bas (respectivement vers le haut, à gauche et à droite). 325 | 326 | Exemple : 327 | ```xml 328 | 330 | 333 | 338 | 339 | ``` 340 | 341 | Cette technique peut par exemple s'appliquer pour faire en sorte que le focus décrive un cercle autour d'un ensemble d'éléments constituant une horloge : à 12h, on souhaite passer à 1h en appuyant sur "droite" ou "bas" et à 11h en appuyant sur "gauche" ou "bas". On aura des relations du type : 342 | ```xml 343 | 361 | ... 362 | 372 | ``` 373 | 374 | ## Afficher la prise de focus 375 | 376 | Il est nécessaire d'indiquer visuellement quel élément possède le focus. Pour cela, il faut créer un `drawable` et lui affecter l'attribut `android:drawable` pour l'état `state_focusable= "true"`. 377 | 378 | Exemple : 379 | ```xml 380 | 381 | ... 382 | 383 | 384 | ``` 385 | 386 | 387 | ## Masquer les éléments décoratifs 388 | 389 | Il est nécessaire de masquer les éléments décoratifs (par exemple une image n'apportant pas d'information supplémentaire dans le contexte dans lequel elle apparaît) pour faire en sorte qu'ils ne soient pas restitués par TalkBack. Pour cela, il faut utiliser l'attribut `android:importantForAccessibility="no"`. 390 | 391 | ## Forcer la restitution des éléments importants non focusables 392 | 393 | Certains éléments non focusables, comme les images non cliquables, ne sont par défaut pas restitués par TalkBack : ce comportement est problématique si ces éléments ne sont pas décoratifs (i.e. qu'ils contiennent des informations essentiels à la compréhension). Il convient alors de forcer leur restitution par les services d'accessibilité en utilisant l'attribut `android:importantForAccessibility="yes"`. 394 | 395 | ## Regrouper des éléments 396 | 397 | Pour faciliter la restitution par les outils d'assistance, il peut être utile de grouper les informations contenues dans plusieurs éléments en une seule description afin de minimiser le nombre d'opérations que devra effectuer l'utilisateur pour y accéder. Par exemple, il est pertinent de regrouper l'intitulé d'un produit, son poids et son prix en un texte qui sera restitué à l'utilisateur en une seule fois. 398 | 399 | Pour cela, il est nécessaire de créer un layout parent avec l'attribut `android:importantForAccessibility="yes"`, de lui associer des fils qui seront créés avec l'attribut `android:importantForAccessibility="no"`, et de placer la description de l'ensemble des éléments regroupés dans l'attribut `android:contentDescription` du layout parent. 400 | 401 | ## Gérer des événements liés à l'accessibilité 402 | 403 | Dans les éléments de base du framework Android, lorsque du contenu est sélectionné, que le focus change ou qu'un élément est survolé, des événements du type `AccessibilityEvent` sont créés. Ces événements sont reçus et interprétés par les services d'accessibilité afin par exemple de fournir des fonctionnalités de restitution vocale aux utilisateurs. 404 | 405 | Générer des événements peut être utile lors de la conception d'une interface utilisateur qui met en oeuvre des vues personnalisées et capturer des événement sert aux services d'accessibilité pour interpréter le comportement d'une application. 406 | 407 | ### AccessibilityEvent 408 | 409 | Un événement peut être généré en utilisant la méthode `sendAccessibilityEvent(int)`, avec un paramètre représentant le type d'événement qui s'est produit. 410 | La liste complète des typées d'événements est disponible dans la description de la classe AccessibilityEvent. Citons par exemple : 411 | 412 | * `TYPE_VIEW_FOCUSED` : déclenché lorsque le focus est positionné sur une vue 413 | * `TYPE_VIEW_CLICKED` : déclenché lorsqu'un clic est réalisé sur une vue (`Button, CompoundButton`, etc.) 414 | * `TYPE_VIEW_TEXT_CHANGED` : déclenché lorsque le texte est modifié dans une zone de saisie (`EditText`) 415 | * `TYPE_ANNOUNCEMENT` : événement "générique" qui peut être utilisé lorsqu'aucun autre type d'événement ne convient, pour indiquer un changement de contexte ou envoyer une information spécifiques destinée à être restituée par les services d'accessibilité. À noter que la classe `View` met à disposition la méthode `announceForAccessibility(CharSequence text)` qui est un moyen simple d'envoyer cet événement. 416 | 417 | Chaque événement dispose de propriétés accessibles via des méthodes permettant de connaître des informations à son sujet lorsqu'il est intercepté par un service d'accessibilité. Par exemple : 418 | 419 | * `getClassName()` : le nom de la classe d'où a été émis l'événement 420 | * `getPackageName()` : le nom du package d'où a été émis l'événement 421 | * `getContentDescription()` : le contenu de l'attribut `contentDescription` de la source 422 | * `isPassword()` : si la source est un champ destiné à recevoir un mot de passe 423 | 424 | D'autres méthodes permettent d'initialiser ou de modifier des propriétés de la source de l'événement. Par exemple : 425 | 426 | * `setContentDescription` : définit la description (correspond à l'information contenue dans l'attribut `android:contentDescription` vue plus haut) 427 | * `setPassword` : définit si un élément est destiné à recevoir un mot de passe 428 | 429 | ### Cheminement des événements 430 | 431 | En plus de la méthode `sendAccessibilityEvent`, la classe `View` propose d'autres méthodes pour gérer les événements liés à l'accessibilité : 432 | 433 | * `sendAccessibilityEventUnchecked` : cette méthode est utilisée lorsque le code appelant gère directement la vérification de l'activation des fonctions liées à l'accessibilité (en appelant `AccessibilityManager.isEnabled()`). 434 | * `onPopulateAccessibilityEvent()` : définit le texte qui sera prononcé lors du déclenchement de l'`AccessibilityEvent` pour la vue concernée. 435 | * `onInitializeAccessibilityEvent()` : méthode permettant d'obtenir des informations supplémentaires concernant l'état de la vue. 436 | * `onInitializeAccessibilityNodeInfo()` : fournit des informations concernant l'état de la vue aux services d'accessibilité. 437 | * `onRequestSendAccessibilityEvent()` : méthode appelée lorsqu'un enfant d'une vue a généré un `AccessibilityEvent`. Cela permet par exemple à la vue parente de modifier l'événement de l'enfant pour y ajouter des informations. 438 | 439 | Pour comprendre le rôle des méthodes mentionnées ci-dessus, il est nécessaire de connaître le cheminement d'un événement : 440 | 441 | * Lorsqu'un service d'accessibilité est activité et que l'utilisateur réalise une action (clic, prise de focus, survol, etc.), la vue est notifiée grâce à un appel à `sendAccessibilityEvent` (ou `sendAccessibilityEventUnchecked`) ; 442 | * Quand elles sont appelées, les méthodes `sendAccessibilityEvent()` et `sendAccessibilityEventUnchecked()` emploient un mécanisme de callback qui fait appel à `onInitializeAccessibilityEvent()` pour initialiser l'événement ; 443 | * Une fois initialisé, si le type de l'événement requiert qu'il soit propagé avec de l'information textuelle, la vue reçoit un appel à `dispatchPopulateAccessibilityEvent()` ; 444 | * Par un mécanisme de callback, `dispatchPopulateAccessibilityEvent()` appelle `onPopulateAccessibilityEvent()` ; 445 | * La vue fait ensuite remonter l'événement dans la hiérarchie des vues en appelant `requestSendAccessibilityEvent()` sur la vue "parent". Chaque vue "parent" a alors la possibilité d'enrichir les informations concernant l'accessibilité en ajoutant une `AccessibilityRecord`, jusqu'à ce que l'événement atteigne la vue racine ; 446 | * Une fois à la racine, l'événement est envoyé à l'`AccessibilityManager` par `sendAccessibilityEvent()`. 447 | 448 | ### Récupérer plus d'informations sur le contexte avec AccessibilityNodeInfo 449 | Le rôle principal d'un événement est d'exposer suffisamment d'information à un service d'accessibilité pour que celui-ci fournisse lui-même des informations pertinentes à l'utilisateur. Parfois, un service d'accessibilité peut avoir besoin de davantage d'information que celle véhiculée par l'événement. Dans ce cas, il est possible de récupérer un objet de type `AccessibilityNodeInfo` qui représente l'état d'une vue, ce qui permet à un service d'accessibilité d'explorer le contenu de la fenêtre. (Pour des raisons de sécurité, l'accès à la source d'un événement est un privilège qui doit être explicitement demandé par une application.) 450 | 451 | `AccessibilityNodeInfo` représente un noeud du contenu de la fenêtre ainsi que les actions qui peuvent être demandées depuis la source. De nombreuses méthodes permettent alors d'explorer le contenu de la fenêtre. Par exemple : 452 | 453 | * `findAccessibilityNodeInfosByViewId` : renvoie une liste d'éléments de type `AccessibilityNodeInfo` correspondant à un identifiant 454 | * `findFocus` : trouve la vue qui possède le focus 455 | (Se référer à la classe AccessibilityNodeInfo pour une liste exhaustive.) 456 | 457 | ## Créer des vues personnalisées accessibles 458 | Le framework Android offre un nombre importants de composants de base : des widgets (`Button, CheckBox`, etc.) et des layouts (`LinearLayout, FrameLayout`, etc.). Il est toutefois possible de créer des composants personnalisés, en étendant la classe `View` : cela permet par exemple de contrôler précisément l'apparence et les fonctionnalités d'un élément. Il est alors nécessaire de s'assurer que ces éléments personnalisés sont accessibles. 459 | 460 | ### Gérer la navigation par contrôleurs directionnels 461 | 462 | Sur la plupart des appareils, cliquer dans une vue en utilisant un contrôleur directionnel envoie un `KeyEvent` de type `KEYCODE_DPAD_CENTER` à la vue qui possède le focus. Toutes les vues Android de base traitent de manière appropriée ce type `KEYCODE_DPAD_CENTER`. Lorsqu'une vue personnalisée est mise en oeuvre, il faut veiller à ce que cet événement ait le même effet que toucher la vue sur l'écran tactile. Par ailleurs, il est aussi nécessaire de traiter l'événement `KEYCODE_ENTER` de la même façon que `KEYCODE_DPAD_CENTER`, ceci afin de faciliter la navigation au clavier. 463 | 464 | Par exemple : 465 | ```java 466 | public boolean onKeyUp(int keyCode, KeyEvent event) { 467 | if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) || (keyCode == KeyEvent.KEYCODE_ENTER)) { 468 | ... 469 | } 470 | } 471 | ``` 472 | 473 | ### Implémenter les méthodes pour l'accessibilité 474 | 475 | Afin de supporter l'accessibilité dans une vue personnalisée, il est nécessaire de surcharger et d'implémenter les méthodes décrites à la fin de la section "Gérer des événements liés à l'accessibilité". 476 | 477 | Avant de donner des exemples d'implémentation de ces méthodes pour créer des vues personnalisées accessibles, il est important de comprendre l'ordre dans lequel sont gérés les événements liés à l'accessibilité. 478 | 479 | #### onInitializeAccessibilityEvent() 480 | 481 | Cette méthode est appelée par le système pour obtenir de l'information concernant l'état d'une vue. Si un composant personnalisé propose des fonctionnalités supplémentaires par rapport à un composant dont il hérite, il convient de surcharger la méthode `onInitializeAccessibilityEvent` et d'ajouter les informations supplémentaires liées au composant personnalisé. Pour surcharger la méthode, il faut d'abord appeler la méthode de la classe héritée et ensuite modifier des propriétés qui n'ont pas été initialisées par la méthode de la classe héritée. 482 | 483 | Supposons par exemple que l'on souhaite étendre la classe `BaseToggleButton` pour faire en sorte que le bouton proposé soit coché par défaut. On aura un code du type : 484 | ```java 485 | public static class AccessibleCompoundButtonInheritance extends BaseToggleButton { 486 | ... 487 | // Cette méthode sera appelée à chaque appel de sendAccessibilityEvent() 488 | @Override 489 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 490 | // On appelle l'implémentation de onInitializeAccessibilityEvent de la 491 | // classe héritée puis on initialise la propriété "checked" qui n'est 492 | // pas présente dans la classe héritée 493 | super.onInitializeAccessibilityEvent(event); 494 | event.setChecked(isChecked()); 495 | } 496 | } 497 | ``` 498 | 499 | 500 | #### onInitializeAccessibilityNodeInfo() 501 | 502 | `onInitializeAccessibilityNodeInfo` permet d'initialiser des informations concernant l'état de la vue qui serviront aux services d'accessibilité lorsqu'ils consulteront la source (type `AccessibilityNodeInfo`) d'un événement. Comme précédemment, cette méthode doit être surchargée pour fournir des informations complémentaires en plus de celles fournies par la classe héritée. 503 | 504 | Les objets de type `AccessibilityNodeInfo` créés par cette méthode sont utilisés par les services d'accessibilité pour explorer la hiérarchie d'une vue qui a généré un événement d'accessibilité après que celui-ci ait été reçu, ce qui permet aux services d'accessibilité d'obtenir plus d'éléments sur le contexte d'apparition de l'événement. 505 | 506 | Une implémentation de `onInitializeAccessibilityNodeInfo` est par exemple : 507 | ```java 508 | public static class AccessibleCompoundButtonInheritance extends BaseToggleButton { 509 | ... 510 | @Override 511 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 512 | super.onInitializeAccessibilityNodeInfo(info); 513 | // On appelle l'implémentation de la classe héritée et on ajoute 514 | // des propriétés: checkable, isChecked, et le texte 515 | info.setCheckable(true); 516 | info.setChecked(isChecked()); 517 | CharSequence text = getText(); 518 | if (!TextUtils.isEmpty(text)) { 519 | info.setText(text); 520 | } 521 | } 522 | } 523 | ``` 524 | 525 | 526 | #### onPopulateAccessibilityEvent() 527 | Chaque événement `AccessibilityEvent` dispose d'un ensemble de propriétés qui décrivent l'état courant de la vue. Lors de la création d'une vue personnalisée, il est nécessaire de fournir des informations sur le contexte et sur les caractéristiques de la vue : ce peut être l'étiquette d'un bouton, mais aussi des informations supplémentaires qui seront fournies à l'utilisateur. 528 | 529 | La méthode `onPopulateAccessibilityEvent()` permet de fournir ces informations supplémentaires sur l'état d'une vue ,: elle est appelée automatiquement par le système pour chaque `AccessibilityEvent`. Cette méthode est donc l'endroit approprié pour décrire l'état d'une vue personnalisée afin notamment de fournir des instructions spécifiques à l'utilisateur de services d'accessibilité. 530 | 531 | Le code suivant permet par exemple de faire prononcer le message "faites glisser" par les services d'accessibilité lorsqu'ils prennent le focus sur la vue : 532 | ```java 533 | public class TestView extends View { 534 | ... 535 | 536 | // Méthode appelée pour propager les événements liés à l'accessibilité. 537 | public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 538 | // On commence par appeler la méthode de la classe héritée 539 | super.onPopulateAccessibilityEvent(event); 540 | // On récupérère le type d'événement 541 | int eventType = event.getEventType(); 542 | // S'il correspond à une prise de focus par un service d'accesibilité... 543 | if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) { 544 | // ... on modifie l'événement pour ajotuer du texte qui sera 545 | // prononcé par le service 546 | event.getText().add("Faites glisser"); 547 | } 548 | } 549 | } 550 | ``` 551 | 552 | ### Gérer le cas des gestes personnalisés 553 | Des vues personnalisées peuvent mettre en oeuvre des interactions gestuelles non standards. Ceci est réalisé en utilisant la méthode `onTouchEvent` pour détecter certains types d'événements. Les services d'accessibilité redéfinissent souvent les gestes de base qui permettent de naviguer à l'intérieur d'une application et les actions qui y sont associés. Il est donc nécessaire de s'assurer que les interactions gestuelles personnalisées resteront compatibles avec les services d'accessibilité, qu'elles permettront de générer des actions qui pourront être interprétées par eux et que ces actions pourront être réalisées par d'autres moyens que l'utilisation de l'écran tactile. 554 | 555 | Le code qui génère la capture des événements tactiles doit réaliser les deux actions suivantes : 556 | 557 | 1. Générer un `AccessibilityEvent` approprié à l'action réalisée par clic 558 | 2. Permettre aux services d'accessibilité de réaliser l'action par un autre moyen que l'écran tactile 559 | 560 | Pour cela, il est nécessaire de surcharger la méthode `performClick()` qui doit d'abord exécuter la méthode de la classe héritée puis exécuter les actions correspondant à l'événement "clic" capturé. La méthode `performClick()` doit ensuite être appelée. 561 | 562 | Par exemple : 563 | ```java 564 | public boolean onTouchEvent(MotionEvent event) { 565 | super.onTouchEvent(event); 566 | switch (event.getAction()) { 567 | case MotionEvent....: 568 | ... 569 | return true; 570 | case MotionEvent....: 571 | // On capture un événement intéressant qui doit causer un clic 572 | // On produit ce clic en appelant performClick() : cela permettra 573 | // aux services d'accessibilité d'en être avertis 574 | performClick(); 575 | return true; 576 | } 577 | return false; 578 | } 579 | 580 | @Override 581 | public boolean performClick() { 582 | // On appelle l'implémentation de la classe héritée qui fait tout le 583 | // travail: générer un AccessibilityEvent et capturer les événements de 584 | // clic avec onClick() 585 | super.performClick(); 586 | // On réalise ensuite les actions particulières à réaliser dans 587 | // la vue personnalisée 588 | blabla(); 589 | return true; 590 | } 591 | ``` 592 | 593 | ## Pour aller plus loin 594 | Il arrive que des implémentations sous forme de vues personnalisées ne permettent pas aux services d'accessibilité de récupérer suffisamment d'éléments de contexte pour restituer l'interface de manière satisfaisante. C'est par exemple le cas d'un ensemble de vues pour lequel une action sur l'une des vues modifie le contenu d'une ou de plusieurs autres vues. Dans ce cas, un service d'accessibilité ne pourra pas être averti qu'une action sur une vue en modifie une autre, car la relation entre les vues n'est pas explicite. Pour contourner ce problème, il est possible de créer des "vues virtuelles" qui ne seront exposées qu'aux seuls services d'accessibilité. Ces vues virtuelles permettent notamment de mieux représenter les informations et le comportement de l'interface et de faire en sorte que les services d'accessibilité aient accès des informations en rapport avec la "logique" de l'application. 595 | La mise en oeuvre d'une vue virtuelle est complexe : elle consiste notamment à étendre la classe `AccessibilityNodeProvider`. Pour plus de détails, nous renvoyons à cet exemple fourni par Google. 596 | 597 | Pour des usages très spécifiques, les services d'accessibilité "standards" tels que TalkBack peuvent ne pas donner entière satisfaction : le concepteur d'une application peut souhaiter que celle-ci ait pleinement la main sur la manière dont seront restitués les éléments de son interface. Par exemple, ce peut être le cas d'applications qui nécessitent de piloter plusieurs outils de synthèse vocale pour distinguer des contenus de natures différentes (ce comportement n'est pas possible avec TalkBack) : utiliser une voix "standard" pour lire le contenu des éléments de l'interface utilisateur de l'application, et une voix de meilleure qualité pour lire le contenu d'un document. l'API d'Android répond à ce besoin en donnant au développeur la possibilité de créer ses propres services d'accessibilité. Le principal rôle d'un tel service sera de traiter les événements d'accessibilité et de les interpréter. Un service possède un type défini qui détermine le retour qu'il fournit à l'utilisateur : `FEEDBACK_SPOKEN, FEEDBACK_BRAILLE, FEEDBACK_VISUAL`, etc. Un service peut capturer tous les événements ou les filtrer selon leur type (capturer uniquement les prises de focus par exemple). Enfin, il peut avoir une portée restreinte à un package ou un ensemble de packages pour se limiter à une application précise, ou bien avoir une portée "globale" pour traiter toutes les applications du système. Pour créer un service, il est nécessaire d'étendre la classe `AccessibilityService`. Cette opération est complexe et nous revoyons à cet exemple d'implémentation de service proposé par Google pour plus de détails. 598 | 599 | ## Guides connexes 600 | 601 | Les guides suivants peuvent être consultés en complément : 602 | 603 | * [Guide d'audit d'applications mobiles](https://github.com/DISIC/guide-mobile_app_audit) 604 | * [Guide de conception d'applications mobiles accessibles](https://github.com/DISIC/guide-mobile_app_conception) 605 | * [Guide de développement d'applications mobiles accessibles avec Ionic et OnsenUI](https://github.com/DISIC/guide-mobile_app_dev_hybride) 606 | 607 | 608 | ## Ressources externes et références 609 | 610 | Sources : 611 | 612 | * API Android 613 | * Making Applications Accessible 614 | * Exemples d'utilisation de l'API Android 615 | * BBC Mobile Accessibility Prototype 616 | 617 | ## Licence 618 | Ce document est la propriété du Secrétariat général à la modernisation de l'action publique français (SGMAP). Il est placé sous la [licence ouverte 1.0 ou ultérieure](http://wiki.data.gouv.fr/wiki/Licence_Ouverte_/_Open_Licence), équivalente à une licence Creative Commons BY. Pour indiquer la paternité, ajouter un lien vers la version originale du document disponible sur le [compte Github de la DInSIC](https://github.com/DISIC). 619 | -------------------------------------------------------------------------------- /ios.md: -------------------------------------------------------------------------------- 1 | # Guide de développement d'applications mobiles accessibles avec l'API iOS 2 | 3 | ## Sommaire 4 | 5 | 6 | * [Sommaire](#sommaire) 7 | * [À qui s'adresse ce guide ?](#%C3%A0-qui-sadresse-ce-guide ) 8 | * [ Généralités sur iOS et l'accessibilité](#g%C3%A9n%C3%A9ralit%C3%A9s-sur-ios-et-laccessibilit%C3%A9) 9 | * [ L'accessibilité dans iOS](#laccessibilit%C3%A9-dans-ios) 10 | * [L'API UI Accessibility](#lapi-ui-accessibility) 11 | * [Les attributs pour l'accessibilité](#les-attributs-pour-laccessibilit%C3%A9) 12 | * [Utiliser les éléments de base fournis par UIKIT](#utiliser-les-%C3%A9l%C3%A9ments-de-base-fournis-par-uikit) 13 | * [Widgets compatibles](#widgets-compatibles) 14 | * [Widgets incompatibles](#widgets-incompatibles) 15 | * [Tester l'accessibilité](#tester-laccessibilit%C3%A9) 16 | * [Déclarer l'accessibilité des vues](#d%C3%A9clarer-laccessibilit%C3%A9-des-vues) 17 | * [Les vues individuelles](#les-vues-individuelles) 18 | * [Les vues "container"](#les-vues-container) 19 | * [Décrire les éléments d'interface](#d%C3%A9crire-les-%C3%A9l%C3%A9ments-dinterface) 20 | * [ Identifier les caractéristiques des éléments d'interface](#identifier-les-caract%C3%A9ristiques-des-%C3%A9l%C3%A9ments-dinterface) 21 | * [Définir la zone de restitution d'un élément](#d%C3%A9finir-la-zone-de-restitution-dun-%C3%A9l%C3%A9ment) 22 | * [Mettre à jour les descriptions et caractéristiques](#mettre-%C3%A0-jour-les-descriptions-et-caract%C3%A9ristiques) 23 | * [Obtenir des informations sur l'état des options d'accessibilité](#obtenir-des-informations-sur-l%C3%A9tat-des-options-daccessibilit%C3%A9) 24 | * [Envoyer des notifications aux outils d'assistance](#envoyer-des-notifications-aux-outils-dassistance) 25 | * [Capturer et traiter les notifications envoyées par UIKit](#capturer-et-traiter-les-notifications-envoy%C3%A9es-par-uikit) 26 | * [Quelques cas particuliers](#quelques-cas-particuliers) 27 | * [Les modales](#les-modales) 28 | * [Les vues tableau (TableView)](#les-vues-tableau-tableview) 29 | * [Guides connexes](#guides-connexes) 30 | * [Ressources externes et références](#ressources-externes-et-r%C3%A9f%C3%A9rences) 31 | * [Licence](#licence) 32 | 33 | ## À qui s'adresse ce guide ? 34 | 35 | Ce guide présente des éléments de l'API d'iOS utiles pour développer des applications accessibles aux personnes en situation de handicap. Il s'adresse : 36 | 37 | * Aux développeurs 38 | * Aux concepteurs en charge de la rédaction de spécifications techniques 39 | * Aux chefs de projets 40 | 41 | Pré-requis : 42 | 43 | * Maîtriser le langage Objective C 44 | * Maîtriser les principes de programmation sous iOS 45 | * Maîtriser les concepts utilisés pour gérer les interfaces utilisateurs sous iOS 46 | * Savoir utiliser XCode 47 | 48 | ## Généralités sur iOS et l'accessibilité 49 | 50 | ### L'accessibilité dans iOS 51 | L'API iOS met à disposition des développeurs des fonctionnalités permettant de créer des applications accessibles. Ces fonctionnalités sont présentes dans les classes qui permettent de créer les éléments de base d'une interface utilisateur ainsi que dans des classes dédiées à l'accessibilité. 52 | 53 | Les applications iOS peuvent être utilisées par des personnes en situation de handicap (visuel, moteur, etc.), grâce à l'activation de fonctionnalités dédiées à l'accessibilité sur l'appareil utilisé pour exécuter les applications et parfois grâce à l'utilisation de périphériques externes (claviers, commutateurs, plages braille, etc.). Les fonctionnalités intégrées directement dans iOS sont par exemple : 54 | 55 | * Zoom : possibilité d'agrandir le contenu de l'écran 56 | * Affichage en blanc sur fond noir (inversion des couleurs) 57 | * Sortie audio en mono seulement : mixe les deux canaux d'un signal sonore en un seul 58 | * Auto-complétion : prononce les suggestions de corrections pendant la saisie d'un texte par l'utilisateur 59 | * Contrôle vocal : permet à un utilisateur d'accéder à certaines fonctionnalités de l'appareil en parlant 60 | * Lecteur d'écran : iOS est livré avec le logiciel VoiceOver qui permet à un utilisateur déficient visuel de contrôler son appareil sans voir l'écran (prononciation du contenu vocalement, redéfinition des gestes, pilotage d'un afficheur braille, etc.) 61 | 62 | Les fonctionnalités d'accessibilité peuvent être activées en se rendant dans le menu "Réglages" de l'appareil, puis dans l'onglet "Accessibilité". 63 | 64 | La simple activation de fonctionnalités d'accessibilité ne suffit pas à rendre une application pleinement accessible : le développeur doit mettre en oeuvre des techniques particulières lors du codage de l'application pour rendre son contenu exploitable par les logiciels prenant en charge l'accessibilité. 65 | 66 | Dans iOS, une application est dite accessible lorsque tous les éléments de son interface avec lesquels l'utilisateur peut interagir sont accessibles, c'est-à-dire lorsque chaque élément se déclare comme un élément porteur d'accessibilité. Par ailleurs, un élément doit porter des informations précises et pertinentes qui pourront être interprétées par les logiciels prenant en charge l'accessibilité : position sur l'écran, nom, comportement, valeur et type. 67 | 68 | ### L'API UI Accessibility 69 | 70 | Depuis la version 3.0, iOS dispose d'une API dédiée à l'accessibilité ("UI Accessibility"). Cette API permet de fournir aux logiciels prenant en charge l'accessibilité toutes les informations utiles pour décrire l'interface utilisateur d'une application. 71 | 72 | L'API UI Accessibility fait partie de UIKit. Ses fonctionnalités sont implémentés par défaut dans les vues et éléments d'interfaces de base d'iOS. Ainsi, des applications accessibles pourront facilement être créées tans que ces éléments de bases seront utilisées. Si des interfaces graphiques plus complexes doivent être créées (par l'utilisation de vues personnalisées), les travaux pour les rendre accessibles seront plus importants. 73 | 74 | L'API UI Accessibility est constituée de deux protocoles, d'une classe, d'une fonction et d'un ensemble de constantes : 75 | 76 | * Le protocole `UIAccessibility` : les objets qui implémentent ce protocole portent des informations relatives à leur accessibilité (à savoir s'ils déclarent être accessibles ou non) et fournissent des informations les décrivant. Les vues et éléments de base implémentent ce protocole par défaut. 77 | * Le protocole `UIAccessibilityContainer` : ce protocole permet à une sous-classe d'UIView de rendre tout ou partie des objets qu'elle contient accessibles en tant qu'éléments distincts. Ceci sert lorsque les objets contenus dans une vue ne sont pas eux-mêmes des sous-classes d'UIView qui ne sont ainsi pas automatiquement considérés comme accessibles. 78 | * La classe `UIAccessibilityElement` : cette classe définit un objet qui peut être renvoyé via le protocole UIAccessibilityContainer. Une instance de UIAccessibilityContainer peut être créée pour représenter un élément qui n'est pas automatiquement considéré comme étant accessible (par exemple, un objet qui n'hérite pas d'UIView). 79 | * Des constantes (définies dans `UIAccessibilityConstants.h`) : ce sont les caractéristiques exposées par un élément et les notifications qu'une application peut envoyer. 80 | 81 | ### Les attributs pour l'accessibilité 82 | 83 | Les contrôles et vues contiennent des attributs permettant de fournir des informations concernant leur accessibilité aux applications d'assistance. Ce sont par exemple : 84 | 85 | * `isAccessibilityElement` : indique si l'élément est accessible. 86 | * `accessibilityLabel` : donne une courte description d'un élément qui permet de comprendre ce qu'il contient ou ce qu'il permet de faire. Cet description ne doit pas comporter d'information concernant le type d'élément. Exemple : "Play", "Ajouter", etc. Le contenu de cet attribut sera lu par VoiceOver à la place du titre du composant s'il en possède un : dans ce cas, il permet d'expliciter ou de préciser le titre du composant. 87 | * `accessibilityLanguage` : indique la langue utilisée pour décrire l'élément. Cette information est utiles pour les outils de synthèse vocale afin que la voix appropriée soit automatiquement sélectionnée. 88 | * `accessibilityTraits` : une contient une combinaison d'une ou plusieurs caractéristiques, qui décrit l'état, le comportement ou le rôle d'un élément. 89 | * `accessibilityHint` : indique une brève description qui permet à l'utilisateur de savoir ce que produit une action sur un élément. 90 | * `accessibilityValue` : la valeur actuelle d'un élément. Par exemple, 30% pour un potentiomètre permettant de choisir le volume du son. 91 | * `accessibilityFrame` : la position d'un élément sur l'écran. 92 | * `accessibilityElementIsHidden` : indique si un élément doit être restitué ou non par VoiceOver. 93 | 94 | 95 | Les éléments porteurs d'accessibilité contiennent ces attributs : ces informations peuvent être fournies dans l'Interface Builder au moment de la création de l'interface ou par le programmeur dans le code Objective C. Les attributs `accessibilityLabel` et `accessibilityFrame` sont toujours requis. L'attribut `accessibilityValue` est utile lorsque le contenu d'un élément est modifiable et qu'il ne peut pas être décrit par `accessibilityLabel`. 96 | 97 | ## Utiliser les éléments de base fournis par UIKIT 98 | 99 | UIKit fournit des éléments de base (widgets) qui implémentent correctement l'accessibilité. Il convient en premier lieu de privilégier l'utilisation de ces éléments et de n'en définir de nouveaux qu'en cas de nécessité. Nous listons ci-dessous des éléments compatibles et non compatibles avec les outils d'accessibilité. Ils ont été testés avec les versions d'iOS 6, 7, 8 et 9. 100 | 101 | 102 | ### Widgets compatibles 103 | 104 | Vues : 105 | 106 | * UIAlertView 107 | * UIPickerView 108 | * UIScrollView 109 | * UITableView 110 | * UIWebView 111 | 112 | Les composants : 113 | 114 | * UIButton 115 | * UIImage 116 | * UILabel 117 | * UISegmentedControl 118 | * UISlider 119 | * UISwitch 120 | * UITextField 121 | 122 | ### Widgets incompatibles 123 | En revanche, les widgets suivants ne sont pas directement compatibles avec les outils d'accessibilité. Ils devront être adaptés au cas par acas : 124 | 125 | * CollectionView 126 | * UIPageControl 127 | * MapKitView 128 | 129 | ## Tester l'accessibilité 130 | 131 | Dans l'API iOS, les fonctionnalités relatives à l'accessibilité sont "stabilisées" depuis la version 5 (il n'y pas de changement majeur dans l'API). Cependant, les outils d'accessibilité (VoiceOver notamment) évoluent rapidement et changent parfois l'interprétation qu'ils font de certains éléments d'interface ou de certaines notifications liées à l'accessibilité. L'interprétation par les services d'accessibilité des widgets courants est relativement stable : les tests réalisés n'ont pas permis de mettre en évidence des régressions. En revanche, l'interprétation de vues personnalisées peut varier, mais ces variations sont extrêmement complexes à documenter. Il est ainsi nécessaire de tester l'accessibilité d'une application iOS afin de vérifier que les recommandations présentées ci-dessous sont bien prises en compte par les services d'accessibilité (voir le [Guide d'audit d'applications mobiles](https://github.com/DISIC/guide-mobile_app_audit)). 132 | 133 | ## Déclarer l'accessibilité des vues 134 | 135 | C'est au développeur de s'assurer de l'accessibilité des vues personnalisées qu'il crée. Il existe deux situations : les vues individuelles et les vues "containers". 136 | 137 | ### Les vues individuelles 138 | Ce sont des vues qui contiennent un unique élément avec lequel l'utilisateur peut interagir. Il est nécessaire de déclarer ces vues comme étant accessibles. Plusieurs moyens sont à disposition : soit dans l'Interface Builder, soit dans le code Objective C de l'application. 139 | 140 | Une première possibilité est de déclarer l'attribut `setIsAccessibilityElement` lors de l'instanciation de la vue : 141 | ``` 142 | @implementation VuePersController 143 | - (id)init 144 | { 145 | _view = [[[VuePers alloc] initWithFrame:CGRectZero] autorelease]; 146 | [_view setIsAccessibilityElement:YES]; 147 | ... 148 | } 149 | ``` 150 | 151 | Une autre option est d'implémenter la méthode `isAccessibilityElement` du protocole `UIAccessibility` : 152 | ``` 153 | @implementation VuePers 154 | - (BOOL)isAccessibilityElement 155 | { 156 | return YES; 157 | } 158 | ``` 159 | 160 | ### Les vues "container" 161 | 162 | Les vues "container" regroupent d'autres vues qui constituent les éléments de l'interface avec lesquels l'utilisateur interagit. Ce sont ces éléments regroupés qui doivent être déclarés comme étant accessibles : la vue "container" ne doit pas être déclarée comme étant accessible, mais doit déclarer la liste des éléments accessibles qu'elle contient. Pour cela, il faut utiliser le protocole `UIAccessibilityContainer`. 163 | 164 | Déclarer la liste des éléments accessibles contenus dans la vue en utilisant `accessibleElements` : 165 | ``` 166 | - (NSArray *)accessibleElements 167 | { 168 | if ( _accessibleElements != nil ) 169 | { 170 | return _accessibleElements; 171 | } 172 | _accessibleElements = [[NSMutableArray alloc] init]; 173 | /* Crée un premier élément et l'initialise comme étant contenu dans la vue */ 174 | UIAccessibilityElement *element1 = [[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self] autorelease]; 175 | /* Ajoute des attributs à cet élément */ 176 | [_accessibleElements addObject:element1]; 177 | /* Crée un deuxième élément... */ 178 | UIAccessibilityElement *element2 = [[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self] autorelease]; 179 | /* Lui ajoute des attributs */ 180 | [_accessibleElements addObject:element2]; 181 | /* etc... */ 182 | ... 183 | return _accessibleElements; 184 | } 185 | ``` 186 | 187 | Il faut ensuite déclarer que cette vue n'est pas accessible : 188 | ``` 189 | - (BOOL)isAccessibilityElement 190 | { 191 | return NO; 192 | } 193 | ``` 194 | 195 | Enfin, implémenter les méthodes `accessibilityElementCount`, `accessibilityElementAtIndex` et `indexOfAccessibilityElement` du protocole `UIAccessibilityContainer` : 196 | 197 | ``` 198 | - (NSInteger)accessibilityElementCount 199 | { 200 | return [[self accessibleElements] count]; 201 | } 202 | 203 | - (id)accessibilityElementAtIndex:(NSInteger)index 204 | { 205 | return [[self accessibleElements] objectAtIndex:index]; 206 | } 207 | 208 | - (NSInteger)indexOfAccessibilityElement:(id)element 209 | { 210 | return [[self accessibleElements] indexOfObject:element]; 211 | } 212 | ``` 213 | 214 | ## Décrire les éléments d'interface 215 | 216 | Un grand nombre d'éléments d'une interface utilisateur fournissent des informations sur leur usage ou sur leur signification grâce à des indications visuelles. Par exemple, une application de prise de note utilise un élément contenant l'image d'un signe "plus" pour indiquer que l'utilisateur peut ajouter une nouvelle note. Une zone de saisie peut disposer d'une indication sous forme d'image qui permet de préciser l'information que l'utilisateur doit renseigner. Un utilisateur aveugle ne peut pas percevoir ces indications visuelles. Il est donc nécessaire d'employer un moyen pour ajouter une alternative à ces indications. Pour cela il faut utiliser les attributs `accessibilityLabel` et `accessibilityHint` dans la définition d'un élément d'interface. Les logiciels d'assistance se basent sur ces attributs pour communiquer aux utilisateurs les informations nécessaires à la bonne compréhension des éléments d'interface. Lorsque la langue des descriptions change, il est nécessaire d'utiliser l'attribut `accessibilityLanguage` en conséquence pour que le texte soit correctement prononcé par VoiceOver. 217 | 218 | Exemple : 219 | ``` 220 | [e1 setAccessibilityLabel:@"Ouvrir"]; 221 | [e2 setAccessibilityLabel:@"Open"]; 222 | [e2 setAccessibilityLanguage:@”en_US”]; 223 | ``` 224 | 225 | NB : les noms propres, les mots d'origine étrangère présent dans le dictionnaire et les mots d'origine étrangère d'usage courant ("play", "OK", etc.) ne doivent pas faire l'objet d'un changement de langue. 226 | 227 | 228 | ## Identifier les caractéristiques des éléments d'interface 229 | 230 | Les caractéristiques permettent de fournir des informations précises aux outils d'assistance sur le comportement et le rôle des éléments d'interface. 231 | 232 | Les éléments de base d'UIKit sont déjà paramétrés avec les caractéristiques appropriées. Si un élément est personnalisé ou lors de la création d'une vue personnalisée, il est nécessaire de déclarer ses caractéristiques. Un élément peut disposer d'une ou de plusieurs caractéristiques qui seront combinées avec l'opérateur `OR`. 233 | 234 | L'API d'accessibilité propose deux catégories de caractéristiques : 235 | 236 | * Celles qui décrivent le type d'un élément (bouton, image, etc.) 237 | * Celles qui définissent le comportement d'un élément (champ de recherche, lancer la lecture d'un son, etc.) 238 | 239 | Les caractéristiques fournies par l'API sont notamment les suivantes : 240 | 241 | * `UIAccessibilityTraitNone` : l'élément n'a pas de comportement défini. Cette caractéristique permet de supprimer toutes les autres (elle ne peut être utilisée en conjonction avec d'autres). 242 | * `UIAccessibilityTraitButton` : l'élément doit être considéré comme un bouton. Le mot "bouton" sera prononcé par VoiceOver après le "label" de l'élément. 243 | * `UIAccessibilityTraitLink` ,: l'élément doit être considéré comme un lien. Le mot "lien" sera prononcé par VoiceOver après le "label" de l'élément. Cette caractéristique doit être utilisée en lieu et place de `UIAccessibilityTraitButton` pour les actions qui ouvrent une page dans un navigateur. 244 | * `UIAccessibilityTraitSearchField` ,;: l'élément doit être considéré comme un champ de saisie permettant d'effectuer une recherche. Cette caractéristique s'applique aux recherches restreintes au contexte de l'application. Par exemple, le champ de saisie d'un moteur de recherche sur le Web ne devra pas posséder cette caractéristique. 245 | * `UIAccessibilityTraitImage` ,;: l'élément doit être considéré comme une image. Le mot "image" sera prononcé par VoiceOver après le "label" de l'élément. Cette caractérisitque peut être combinée avec d'autres (`UIAccessibilityTraitButton` ou `UIAccessibilityTraitLink` par exemple). `UIAccessibilityTraitImage` doit être utilisée lorsque l'apparence de l'image véhicule une information essentielle à la compréhension : elle ne doit pas être utilisée pour les images de décoration. Pour les boutons intégrés sous forme d'une image dont l'apparence n'apporte pas d'information essentielle à la compréhension de l'action, seule `UIAccessibilityTraitButton` devra être utilisée. 246 | * `UIAccessibilityTraitSelected` : l'élément doit être considéré comme sélectionné. Le mot "sélectionné" sera prononcé par VoiceOver après le "label" de l'élément. Cette caractéristique pourra par exemple être utilisée pour indiquer l'état d'un panneau dans un système d'onglets (tabpanel). 247 | * `UIAccessibilityTraitPlaysSound` : l'élément permet de lancer la lecture d'un son. 248 | * `UIAccessibilityTraitKeyboardKey` : l'élément se comporte comme une touche de clavier. 249 | * `UIAccessibilityTraitStaticText` : l'élément doit être considéré comme du texte statique, qui ne peut pas être modifié. 250 | * `UIAccessibilityTraitSummaryElement` : représente un résumé de l'état courant d'un élément restitué lorsque l'application démarre. (NB : le focus n'est pas placé sur cet élément ; le résumé n'est restitué qu'une fois à l'ouverture de l'application, mais pas à chaque nouvel affichage de la page concernée). Cette caractéristique peut ainsi être utilisée pour attirer l'attention de l'utilisateur de VoiceOver sur un élément important de la page d'accueil qui n'apparaît pas immédiatement dans l'ordre des éléments focusables. 251 | * `UIAccessibilityTraitNotEnabled` : indique qu'un élément est désactivé ou n'est pas destiné à répondre aux interactions. Cette caractéristique peut par exemple être utilisée dans le cas où un bouton appraît dans l'interface mais dont le fonctionnement est temporairement désactivé. `UIAccessibilityTraitNotEnabled` peut être combinée avec d'autres caractéristiques (comme `UIAccessibilityTraitButton`). 252 | * `UIAccessibilityTraitUpdatesFrequently` : indique que le contenu de l'élément est mis à jour trop fréquemment pour qu'il soit pertinent d'envoyer des notifications aux logiciels d'assistance à chaque changement. Cela permet d'éviter de "saturer" le contenu restitué par VoiceOver lorsqu'un élément change très rapidement (le contenu affiché par un chronomètre par exemple). 253 | * `UIAccessibilityTraitStartsMediaSession` : indique que l'élément permet de lancer la lecture d'un contenu multimédia et ainsi d'indiquer à VoiceOver qu'il ne doit pas utiliser la synthèse vocale (la lecture du média pourra ainsi être lancée sans être perturbée par un texte énoncé par VoiceOver). 254 | * `UIAccessibilityTraitAdjustable` : indique que la valeur d'un élément (comme un slider) peut être ajustée. Le mot "ajustable" sera prononcé par VoiceOver après le label, ainsi que le message "glissez vers le haut ou vers le bas pour modifier la valeur". L'utilisation de cette caractéristique provoque une modification du comportement par défaut de VoiceOver, qui redéfinit les gestes de "glissement" vers le haut et vers le bas pour ajuster la valeur de l'élément : l'action par défaut de ces gestes (par exemple la navigation de titre en titre) n'est donc plus disponible dans ce contexte.. 255 | * `UIAccessibilityTraitAllowsDirectInteraction` : représente un élément avec lequel l'utilisateur peut interagir directement via l'écran tactile. Lorsque cette caractéristique est activée, VoiceOVer n'interprète plus les gestes de l'utilisateur et laisse cette interprétation à la charge de l'application. 256 | * `UIAccessibilityTraitCausesPageTurn` : représente un élément qui provoque automatiquement le passage à la page suivante lorsque les outils d'assistance arrivent à la fin du contenu. L'utilisation de cette caractéritique est par exemple pertinente dans une applicaiton de lecture de livre numérique pour automatiser le passage d'une page à la suivante lors de la restitution du texte par VoiceOver, sans que l'utilisateur n'ait à intervenir manuellement. 257 | * `UIAccessibilityTraitHeader` : indique que l'élément possédant cette caractéristique permet de diviser du contenu (titre, barre de navigation, etc.). Son utilisation permet à VoiceOver de faciliter la navigation à l'intérieur de l'application. 258 | 259 | Les caractéristiques peuvent être définies lors de l'initialisation d'une vue : 260 | ``` 261 | - (id)init 262 | { 263 | _view = [[VuePers alloc] initWithFrame:CGRectZero]; 264 | ... 265 | [_view setAccessibilityTraits:UIAccessibilityTraitButton | UIAccessibilityTraitPlaysSound]; 266 | ... 267 | } 268 | ``` 269 | 270 | Les caractéristiques peuvent aussi être initialisées en définissant la méthode `accessibilityTraits` : 271 | ``` 272 | - (UIAccessibilityTraits)accessibilityTraits 273 | { 274 | return UIAccessibilityTraitButton | UIAccessibilityTraitPlaysSound; 275 | } 276 | ``` 277 | 278 | ## Définir la zone de restitution d'un élément 279 | 280 | Il est possible de délimiter la zone de restitution d'un composant, c'est-à-dire la zone à l'intérieur de laquelle une interaction tactile déclenchera la restitution du contenu de ce composant par VoiceOver. Ceci peut être utile pour limiter le risque de confondre l'élément avec des éléments voisins proches lors de l'exploration tactile de l'écran. Pour cela, il faut utiliser la fonction `setAccessibilityFrame`. 281 | 282 | Exemple : 283 | ``` 284 | [_c setAccessibilityFrame:CGRectMake(10,10,130,130)]; 285 | ``` 286 | 287 | ## Mettre à jour les descriptions et caractéristiques 288 | 289 | Lorsque les éléments d'interface d'une application changent, il est nécessaire de mettre à jour leurs descriptions et caractéristiques. Les méthodes permettant d'indiquer les attributs utiles pour l'accessibilité (`accessibilityLabel`, `accessibilityTraits`, etc.) doivent donc retourner des valeurs en fonction du contexte précis de l'élément et non "initialisées une fois pour toutes". 290 | 291 | ## Obtenir des informations sur l'état des options d'accessibilité 292 | 293 | iOS met à disposition des méthodes permettant de récupérer des informations concernant l'état de certaines options d'accessibilité afin d'ajuster le comportement du programme en conséquence. 294 | 295 | Par exemple : 296 | 297 | * `UIAccessibilityIsVoiceOverRunning` : indique si VoiceOver est actif. 298 | * `UIAccessibilityIsMonoAudioEnabled` : indique si le système audio est en mode mono. 299 | * `UIAccessibilityIsClosedCaptioningEnabled` : indique si le sous-titrage est activé. 300 | 301 | 302 | 303 | ## Envoyer des notifications aux outils d'assistance 304 | 305 | Lorsqu'un élément d'interface change, il est nécessaire d'envoyer une notification afin que les outils d'assistance soient informés de la modification. Des notifications doivent être envoyées lorsqu'un changement "significatif" se produit : il faut éviter de surcharger les outils d'assistance d'informations qu'ils ne seraient pas en mesure de restituer en temps réel. 306 | 307 | L'envoi de notifications destinées aux outils d'assistance est réalisé avec la méthode `UIAccessibilityPostNotification`. 308 | 309 | Par exemple, pour notifier le changement d'un élément d'interface : 310 | ``` 311 | UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); 312 | ``` 313 | 314 | `UIAccessibility` fournit des notifications qui peuvent être envoyées par les applications, parmi lesquelles : 315 | 316 | * `UIAccessibilityLayoutChangedNotification` : permet d'envoyer une "annonce" destinée à être restituée par les outils d'assistance (un message peut être passé via une chaîne de caractère en paramètre). Cette notification doit être utilisée pour avertir les utilisateurs de VoiceOver du changement d'un élément de l'interface. 317 | * `UIAccessibilityAnnouncementNotification` : permet d'envoyer une "annonce" destinée à être restituée par les outils d'assistance (un message peut être passé via une chaîne de caractère en paramètre). Cette notification doit être utilisée pour avertir les utilisateurs d'un événement qui n'a pas modifié d'élément d'interface ou qui en a modifié pour une durée brève. 318 | * `UIAccessibilityPageScrolledNotification` : permet de fournir de l'information sur le contenu de l'écran après qu'un utilisateur ait "scrollé" avec VoiceOver. 319 | * `UIAccessibilityPauseAssistiveTechnologyNotification` et `UIAccessibilityResumeAssistiveTechnologyNotification` : permet de stopper temporairement et de recommencer les actions réalisées par les outils d'assistance spécifiés en paramètre. 320 | * `UIAccessibilityScreenChangedNotification` : permet de notifier les outils d'assistance lors de l'apparition d'une nouvelle vue qui prend une part importante de l'écran. Une chaîne qui sera restituée par les outils d'assistance ou bien un élément d'interface sur lequel sera positionné le focus de l'outil d'assistance utilisé peuvent être passés en paramètre. 321 | 322 | ## Capturer et traiter les notifications envoyées par UIKit 323 | 324 | UIKit envoie des notifications relatives à l'accessibilité qu'il peut être utile de traiter pour améliorer le comportement de l'application. Capturer ces notifications permet de personnaliser le comportement de l'application pour les utilisateurs d'outils d'assistance. 325 | 326 | Ces notifications peuvent être capturée via le centre de notifications. Par exemple, pour capturer la notification `UIAccessibilityVoiceOverStatusChanged` : 327 | ``` 328 | [[NSNotificationCenter defaultCenter] 329 | addObserver:self 330 | selector:@selector(updateUserInterfaceForVoiceOver:) 331 | name:UIAccessibilityVoiceOverStatusChanged 332 | object:nil]; 333 | ``` 334 | 335 | Les notifications relatives à l'accessibilité fournies par l'API sont notamment les suivantes : 336 | 337 | * `UIAccessibilityVoiceOverStatusChanged` : envoyée par UIKit lorsque VoiceOver démarre ou est arrêté. 338 | * `UIAccessibilityAnnouncementDidFinishNotification` : envoyée par UIKit lorsque la restitution d'une annonce par les outils d'assistance est terminée. Cette notification renvoie un "dictionnaire" contenant deux clés, `UIAccessibilityAnnouncementKeyStringValue` et `UIAccessibilityAnnouncementKeyWasSuccessful`, qui permet de connaître le message qui a été restitué et de savoir si la restitution s'est terminée avec succès ou non. 339 | 340 | ## Quelques cas particuliers 341 | 342 | ### Les modales 343 | 344 | Les vues d'UIKit qui permettent de mettre en oeuvre des modales (`UIAlertView` par exemple) sont accessibles : elles permettent à l'utilisateur d'outils d'assistance de ne pas interagir avec le contenu situé en dehors de la vue. Pour les vues personnalisées, il faut utiliser l'attribut `accessibilityViewIsModal` : lorsque cet attribut vaut "YES" les éléments situés à l'intérieur des vues voisines seront ignorées par les outils d'assistance. 345 | 346 | ### Les vues tableau (TableView) 347 | 348 | Les cellules d'une vue en tableau sont considérées automatiquement comme des containers : il n'est donc pas nécessaire d'implémenter le protocole `UIAccessibilityContainer` pour déclarer la liste des éléments contenus. 349 | Pour faciliter la restitution par les outils d'assistance, il peut être utile de grouper les informations contenues dans plusieurs cellules en une seule description afin de minimiser le nombre d'opérations que devra effectuer l'utilisateur pour y accéder. Par exemple, il est pertinent de regrouper l'intitulé d'un produit, son poids et son prix en un texte qui sera restitué à l'utilisateur en une seule fois, sans qu'il n'ait à réaliser des gestes pour accéder à chacun des éléments séparément. 350 | 351 | La méthode `accessibilityLabel` pourra alors être définie de la sorte : 352 | ``` 353 | - (NSString *)accessibilityLabel 354 | { 355 | NSString *prodNomText = [self.prodNom accessibilityLabel]; 356 | NSString *prodPoidsText = [self.prodPoids accessibilityLabel]; 357 | NSString *prodPrixText = [self.prodPrix accessibilityLabel]; 358 | 359 | return [NSString stringWithFormat:@"%@, %@, %@", prodNomText, prodPoidsText, prodPrixText]; 360 | } 361 | ``` 362 | 363 | 364 | ## Guides connexes 365 | 366 | Les guides suivants peuvent être consultés en complément : 367 | 368 | * [Guide d'audit d'applications mobiles](https://github.com/DISIC/guide-mobile_app_audit) 369 | * [Guide de conception d'applications mobiles accessibles](https://github.com/DISIC/guide-mobile_app_conception) 370 | * [Guide de développement d'applications mobiles accessibles avec Ionic et OnsenUI](https://github.com/DISIC/guide-mobile_app_dev_hybride) 371 | 372 | ## Ressources externes et références 373 | 374 | Sources : 375 | 376 | * UIKit Framework Reference 377 | * Accessibility for iOS Developers 378 | * BBC Mobile Accessibility Prototype 379 | 380 | ## Licence 381 | Ce document est la propriété du Secrétariat général à la modernisation de l'action publique français (SGMAP). Il est placé sous la [licence ouverte 1.0 ou ultérieure](http://wiki.data.gouv.fr/wiki/Licence_Ouverte_/_Open_Licence), équivalente à une licence Creative Commons BY. Pour indiquer la paternité, ajouter un lien vers la version originale du document disponible sur le [compte Github de la DInSIC](https://github.com/DISIC). 382 | --------------------------------------------------------------------------------