A more accessible audio player
Learning to build with empathy for people with blindness and vision impairments
Author: Francis Mariano, iOS Engineer
Improve the health and happiness of the world — a bold mission statement we have at Headspace. One that forces us to recognize the diverse experiences of those who inhabit it. For the majority of us, that experience allows us the opportunity to appreciate the rich aesthetics of our app. From crisp illustrations and playful animations to vibrant, evocative colors, there’s no doubt that our designers have worked hard to create these pleasing visuals.
All that and more came into play when we decided to redesign our audio player, one of the core features of Headspace. We wanted to build an immersive experience that still felt natural to our members. The changes involved adding intuitive playback controls, an animated background, a new settings drawer, along with some refactoring of our business logic to handle the different types of content we have available. Given that this was the first major project I worked on as an iOS Engineer, it felt rewarding to see these changes finally go live. What I learned the most, though, came not from struggling to correctly set a view’s constraints or from chaining the right series of methods in RxSwift, but from spending a bit of time trying to understand how certain people — those who interact with the world differently from me — would use our app.
We received feedback expressing irritation from some of our members with blindness and vision impairments over how inaccessible the redesign was, and from that feedback, we realized there was a lot we could do to improve the overall VoiceOver experience for our audio player. What follows is a series of corrections and optimizations made for our audio player around accessibility in the visual domain and the learnings obtained along the way.
A brief overview of VoiceOver
VoiceOver is Apple’s native screen reader that allows visually-impaired members to understand what’s happening on their devices. Anytime that someone taps on an accessibility element on the screen, the element will be focused, and VoiceOver will provide a description of that element. We can control what VoiceOver says by setting an element’s properties: a label, a trait, and a hint. For example, if we have “X” icon somewhere on the screen with a label set as “Close,” a trait set as “Button,” and a hint set as “Dismisses the modal,” VoiceOver will read the following when a member taps on the icon: “Close, Button, Dismisses the modal.” A member can then activate the element by double-tapping with one finger anywhere on the screen. Doing so is the equivalent of a single tap when VoiceOver is off. Later on, I’ll discuss some other gestures available to us that can make interacting with our app easier.
These properties and gestures are simple, but how we implement them can greatly impact the accessibility of our app. As you’ll see in the examples below, element mislabeling and forgetting to add hints can result in confusion, even frustration. When implemented properly, however, we make the audio player a more inclusive experience.
Improvements
Accessibility label for the play/pause button
Typically with a play/pause button, the displayed icon should reflect the action that occurs when the button is tapped, rather than describe the state of the audio. If the button, for example, displays the pause icon, that means tapping on the button will pause the audio. This visual communication, though, was not translated accurately in our VoiceOver experience. When audio was playing, VoiceOver would say “Play” when the button was selected, and vice-versa when audio was paused — the opposite of what someone would expect.
By switching the accessibility labels for these two states, we avoid any confusion as to what happens when the button is activated, which is especially important for the moments of silence during our meditations.
Settings button accessibility hint
One of the main updates to the audio player was the relocation of a session’s duration options from directly on the player to a modal that opens when the settings button is tapped. Since our members were likely to wonder where the duration options went upon landing on this updated player for the first time, we also added a tooltip above the settings button to notify them of these changes.
What we failed to notice was how our visually impaired members would perceive this change. For custom views that do not inherit from a UIKit control, such as this tooltip, we have to explicitly set a view’s “isAccessibilityElement” property to true; otherwise, VoiceOver will not be able to select the element at all. By overlooking this property, we left our members unaware of the options’ relocation and likely gave the impression that a session’s duration could no longer be changed or that our player did not function correctly with VoiceOver enabled.
While we could have set the tooltip as an accessibility element, we also wanted our members to experience less friction when interacting with the player. Less taps means reaching one’s purpose more quickly. Since the tooltip serves more as a visual aid and as an accessory to the settings button, it made more sense to use the tooltip’s text as the settings button’s accessibility hint and to hide the tooltip if VoiceOver were turned on. With this simple change, it becomes clear where our duration selection has moved to.
Element focus on layout changes
When a new view opens or closes, by default VoiceOver will select the topmost element in the view hierarchy. In our case, this meant that whenever the audio player opened or a member dismissed the setting drawer, the close button would be selected. This behavior is counterintuitive; someone doesn’t open our audio player or select a duration only to close the player immediately. When such actions are performed, we want to highlight the element that performs the view’s main action, which in this case is the player button. This can be achieved by using the “screenChanged” and “layoutChanged” notifications with “UIAccessibility.post(notification:argument:).” That way, once the player opens or a duration has been selected, all the member has to do is double-tap to get the session started.
Magic taps and escape gestures
UIKit comes with some gestures out of the box that make interacting with an app much easier. Once audio starts playing, a member can perform a two-finger double-tap (also called a magic tap) to toggle between play and pause. This gesture can easily be added by overriding the “accessibilityPerformMagicTap()” method in your view controller. A member can also perform what is called the escape gesture, an N-shaped swipe on portrait mode or Z-shaped swipe on landscape mode, which closes the currently presented modal. Like with the magic tap, one can implement this gesture by overriding the view controller method “accessibilityPerformEscape().” These gestures are two quick, low-effort ways to improve an app’s usability and should be added to any heavily-used view.
Learnings
What started out as a simple fix to the accessibility of our new settings button led to a more optimized experience with some valuable lessons around shaping user experience. After exploring the available methods around accessibility on iOS and diving more into this topic, I discovered that there is more to being an engineer than just writing code, constructing a user interface, and stitching together APIs. To properly bring the member experience to life requires a lot of empathy: the ability to step away from one’s own limited perceptions for a moment in order to understand the experiences that those limitations make it difficult to recognize. Such empathy is important when you’re working on an app that many people rely on to ease their anxieties or help them get better sleep. Providing the benefits of Headspace to those who have blindness or vision impairments is just one part of following through on our mission statement, and I’m happy to say that more work is to come in making our content more accessible.