I have recently been tasked with the implementation of accessibility related to blindness on a few different iOS projects. There is a lot of documentation on this already, but finding it all in one place and getting a “quick” overview of the basic idea were a bit harder to find. Following are the notes I made for myself as well as some tips and gotchas that others might benefit from.
To test accessibility for blind users, you must first enable a feature on your device. Open the “Settings” app, then navigate to “General -> Accessibility -> VoiceOver” and enable the toggle for “VoiceOver”. There are a bunch of options such as speaking rate that you can modify to your liking.
You should also see some tips:
- Tap once to select an item
- Double-tap to activate the selected item
- Swipe three fingers to scroll
I have also found a few other important input gestures:
- One finger swipe right or left to shift the selection focus of screen items
- One finger swipe up or down to increment or decrement adjustable items (like a page control)
As well as some opt-in gestures we could use for better experiences:
- Draw a “Z” shape with two fingers to exit a screen (i.e. “back” for a navigation controller)
- Two finger double tap performs a “magic tap” which starts or stops an action
Note that several of these gestures are not location specific. For example, you can double tap anywhere on the screen to activate a selected button.
For a more complete list of gestures see the documentation: Verifying App Accessibility on iOS -> Test Accessibility on Your Device with VoiceOver
To better experience what it would be like for a blind user you can use another feature called the “screen curtain” which turns the display off. Do a three-finger triple-tap on your screen to toggle this feature. This is very helpful to test whether one can truly understand the contents of a screen without the visual cues of its layout.
The simplest way to add accessibility to your app is to simply use native iOS views and components. Labels, Buttons, TableViews, etc all work right out of the box without any extra effort. However, some native implementations may still require a little extra help such as if you have a button that only displays an image, or use an image to display text.
When necessary, use the “Identity Inspector” (option+command+3) and you will find an “Accessibility” section at the bottom. You can enable or disable accessibility with the “enabled” check box, or provide a custom “Label” or “Hint”. The “Label” is the short description of an item that will be read by the screen reader when selected. Do not add words like “Button” to your label, those will automatically be appended by “Traits”. The “Hint” gives you a chance to elaborate on what will happen when activating a selected item. Since the user may not be able to take advantage of the visual ques that a sighted person will notice, the hint may provide necessary context to the action.
Whether an item is accessible or not, labels, and hints are all properties that can be edited programmatically:
item.isAccessibilityElement = true item.accessibilityLabel = "Edit" Item.accessibilityHint = "Double tap to edit your profile"
Traits can also be added to elements through the “Identity Inspector” or programmatically. The traits are implemented as a bit mask such as in the example below:
// Add the "selected" trait item.accessibilityTraits |= UIAccessibilityTraitSelected // Remove the "selected" trait item.accessibilityTraits &= ~UIAccessibilityTraitSelected
If you need full control over which items are read, or want control over the order that elements receive focus through swipe navigation, you can implement a parent view as a UIAccessibilityContainer.
If you have implemented a custom view and need to respond to accessible gestures such as three finger scrolling, magic taps, etc. then you can also choose to implement the UIAccessibilityAction protocol.
If you have a super customized view or gesture recognition input that is incompatible with the screen reader, you can always provide an alternate or modified interface while the screen reader is running. You can call a method “UIAccessibilityIsVoiceOverRunning” to determine this state and can also listen for a notification “UIAccessibilityVoiceOverStatus” just in case that state should change.
There were a handful of unexpected moments and things that could be easy to overlook as well:
- The screen reader doesn’t always read words correctly. Important or brand-specific words may require you to use custom labels with modified spellings to cause the screen reader to better approximate the correct pronunciation.
- The screen reader especially struggles with ALL CAPS TEXT. A common problem is that it will read each letter instead of the word itself, such as “ADD” becoming “A.D.D.”. I think a best practice work around would be to use a font that only displays uppercase glyphs regardless of the string that you pass it. This can allow you to pass a string with normal case but render it as the design requires.
- Accessibility Hints and Labels will also need to be localized.
- Just because a view is not visible to the eye, does not mean it wont be visible to the screen reader. If you intend to “hide” elements on screen by covering it with another view then either toggle the “isAccessibilityElement” flag for covered items, or toggle the “accessibilityElementsHidden” of their container view.
- One-finger swipe navigation to a collection view causes it to automatically scroll to the beginning or end (depending on the direction you were navigating). This can be problematic for things like a page-control driven element because the page-control value and the visible element will no longer be in sync.
- Putting multiple links into an attributed text view can make it difficult for a user to activate a particular link. I find it more intuitive to interact with if the links are provided in separate elements.
- Custom gesture recognizers will likely be overridden by gestures used by the screen reader. You will need to plan for this and provide an alternate means of performing the necessary actions.
- The frame of an accessibility element matters. If you scale something to zero, it can break the one-finger swipe navigation on the screen such as by getting stuck at that element. If you hide an element by sizing it to zero, then you should also use some method to disable accessibility for it.
Accessibility is a very important issue that many developers aren’t aware of or experienced with. This post serves as a very quick introduction for how to use the feature on a phone, how to implement it using Xcode or through code, and a couple of tips and warnings to keep an eye out for.