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.

5 (3) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

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:

List view with improved color descriptions

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:

Tree of ListCellView's button and color description elements

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.

Note: Apple is actively working on many aspects of its Accessibility API and iOS Accessibility options. A new Xcode or iOS update might fix something, but might break something else. Please report bugs in this article’s forum and in Apple’s developer forums. And please be patient: “Everything will be alright in the end, and if it is not alright, it is not yet the end.” (The Best Exotic Marigold Hotel)

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:

Tree showing reordered HStack

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.

Note: If you prefer, just replace the 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.