iOS Accessibility in SwiftUI Tutorial Part 2: Organizing
In this accessibility tutorial, you’ll organize the accessibility information of a SwiftUI app by restructuring its accessibility tree. By Audrey Tam.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
iOS Accessibility in SwiftUI Tutorial Part 2: Organizing
30 mins
- Getting Started
- Color Contrast Ratio
- Calculating Color Contrast Ratio
- Better Labels for All Users
- Creating Labels to Reduce Jargon
- Modifying the Accessibility Tree
- Changing the Order for VoiceOver
- Containing Child Elements
- Using VoiceOver on a Device: Swipe Right
- Combining Child Elements
- Ignoring Child Elements
- Challenge: Reordering Exercise
- Making Sense of Sliders
- Fixing the Contrast Ratio View
- Fixing the Sliders
- Using VoiceOver on a Device: Sliders
- Headings for Faster Navigation
- Rotating the VoiceOver Rotor
- Creating Headings
- Where to Go From Here?
Creating Labels to Reduce Jargon
First, you’ll fix the color descriptions. Instead of the Color
element’s built-in description
, you’ll add a listener-friendly description property to ColorModel
.
In ContrastModel.swift, add this computed property to struct ColorModel
:
var accDescription: String {
"Red \(rInt), Green \(gInt), Blue \(bInt)."
}
You’ll replace the hex value with the three red, green and blue integer values. The punctuation will cause VoiceOver to pause in the right places.
Here’s a light-bulb moment 💡: These values are as useful for sighted users as they are for VoiceOver users, so why not display them in the UI? But the accDescription
string would take up too much room. You can get away with just R
, G
and B
for the visual display, and you don’t need punctuation.
So, add another computed property to struct ColorModel
:
var description: String {
"R\(rInt) G\(gInt) B\(bInt)"
}
And now put these descriptions to work. In ContrastListView.swift, in struct ListCellView
, replace the contents of the HStack
with this code:
Text("Text \(contrast.text.description)")
.accessibility(label: Text("For Text color "
+ contrast.text.accDescription))
Text("Bkgd \(contrast.bkgd.description)")
.accessibility(label: Text("on Background color "
+ contrast.bkgd.accDescription))
Text("Ratio " + contrast.ratio())
You’ve replaced the colorView.description
with your new ColorModel
descriptions, and provided more descriptive accessibility labels for the text and background colors. You’ve also removed the colons from the displayed text, to help it fit better, but also so VoiceOver doesn’t pause between Ratio and the ratio value. And you’re displaying an abbreviation for Background. It’s OK; you’ve also specified an accessibility label with the full word Background, so VoiceOver won’t spell out b-k-g-d.
Build and run. The descriptions fit on an iPhone 8 screen:
Then listen to VoiceOver:
For Text color Red 163, Green 17, Blue 129 on Background color Red 181, Green 120, Blue 205. Ratio 2.20.
That sounds totally awesome! I could listen to that all day. Except the Ratio part at the end sounds awkward. But you know you’re going to fix that now ;].
Modifying the Accessibility Tree
In this part, you’ll change the order that VoiceOver visits elements and hide elements that provide redundant information. You’ll also group elements to reduce the number of steps for a VoiceOver user or to move some of the information to hints that your VoiceOver users don’t have to listen to.
The elements in your app’s UI form a tree hierarchy of views in container stacks:
There’s a corresponding accessibility tree. At the moment, it looks exactly like your app’s UI tree. The framed purple elements are the accessible elements.
The great thing is: The accessibility API gives you the power to modify the accessibility tree, to provide better information and more efficient navigation to your VoiceOver users. And, it lets you do this with no changes to your app’s UI tree.
Changing the Order for VoiceOver
The quickest way to get VoiceOver to say the ratio value before the colors is to move the Ratio Text
element ahead of the Text and Background Text
elements in the HStack
.
But suppose you think your sighted users prefer to see the ratio value at the trailing edge, because it’s easy to run your eye down the screen edge, to see all the ratio values. It’s more helpful for VoiceOver users to hear the ratio first, so you need to change the order for VoiceOver, while keeping it the same for sighted users.
This is an opportunity to use accessibility(sortPriority:)
. You’ll increase the sort priority of the Ratio Text
element for VoiceOver, without moving it from where it appears on screen. This modifies the HStack
part of the accessibility tree:
First, in ContrastListView.swift, in struct ListCellView
, add this modifier to the Ratio Text
element:
.accessibility(sortPriority: 1)
The default sortPriority
is 0, so increasing it to 1 for the Ratio Text
element is enough to make VoiceOver say it before the Text and Background Text
elements.
Containing Child Elements
Next, add this modifier to the HStack
:
.accessibilityElement(children: .contain)
You can use the accessibilityElement
modifier to .contain
, .combine
or .ignore
the accessible child elements in a stack, and you’ll soon use .combine
and .ignore
. Actually, .contain
is its default behavior — it means: Treat accessible child elements as individual elements. But, at the time of writing this article, accessibility(sortPriority:)
doesn’t work correctly without it.
Finally, tell VoiceOver to describe the button’s action instead of saying the sample quick brown fox text. Add this modifier to the quick-brown-fox Text
element:
.accessibility(label: Text("Edit colors"))
This replaces the button’s label for VoiceOver. Now your VoiceOver users don’t have to listen to the quick brown fox sample text. Instead, they’ll hear what tapping this button does.
Text
element’s The quick brown fox ...
with Edit colors
. Then you don’t need a separate accessibility(label:)
, and the button is immediately visible to non-VoiceOver users. Thinking about what’s good for VoiceOver users can help improve your app for all your users!
Now build and run, and listen to VoiceOver say Edit colors. Button., then read Ratio … for Text color … on Background color ….
Using VoiceOver on a Device: Swipe Right
In the accessibility inspector, when you click the auto-navigate (Play) button, the VoiceOver simulator reads the accessible element labels one after the other, continuing through all the list items. But on a device, a VoiceOver user must swipe-right after VoiceOver says Ratio … to hear for Text …, then swipe-right again to hear on Background ….
Again, you need to run your app on a device, to find out how it really behaves and sounds for a VoiceOver user.
If necessary, adjust the iOS Deployment Target, change the Bundle Identifier organization, and select a Team under Signing & Capabilities.
On your device, check your Settings▸Accessibility▸Accessibility Shortcut is set to VoiceOver.
Build and run the app on your device. Triple-click the side button to start VoiceOver. Tap one of the Edit-color buttons, then swipe right: VoiceOver reads the ratio value and stops. Swipe right to hear the text color value, then swipe right again to hear the background color value.