SwiftUI is almost here! For the past 6 months I have been preparing iOS video courses for raywenderlich.com. Mid-way Apple suddenly announced the amazing new UI Framework SwiftUI! The world stopped, everyone gasped and the learning fest began! There are so many things to talk about, however one of the most amazing achievements by the team at Apple is the seamless and effortless integration of features that assistive users require in order to use your apps.

Accessibility is for everybody and thanks to SwiftUI’s declarative style it is incredibly simple to integrate features that users who require assistive technology rely on. A lot of the time they’ll be using features such as VoiceOver: a screen reader built right into every part of iOS. Thankfully, SwiftUI gives you VoiceOver support for free a lot of the time, but there are a few things to know that will help you make the experience as fun as possible for all of your users.

Context – Helping Users to Understand your UI

When designing user interfaces it is tempting to do so from a purely-visual point-of-view. This makes sense as we want our apps to look beautiful, elegant and attractive to our users. But how does this UI ‘look’ to users who don’t really observe your UI elements visually? Is it an elegant or frustrating experience? Apple did a stellar job of making VoiceOver a priority at WWDC 2019, even showcasing the experience live when a user is navigating with it throughout several talks.

One of the most interesting was getting developers to think: ‘how do my users get the context of what is going on here’? The example given was a calculator: when it appeared on the screen the context was numbers from 0-9, operators such as ‘+’ a ‘x’ as well as numbers showing output on the top. If you saw this interface for the first time, the context of what is going on might seem fairly straightforward. But what if you needed calculator functionality but couldn’t see the screen, would it be quite so simple? Bare in mind that this is a very simple interface, most apps have a much more complex layout.

In this blog I want to show 3 great lessons I’ve learned in building out my first SwiftUI-only app supporting Accessibility from the start.

Accessibility Headers – Rapidly scan your UI

One of the first steps you would usually take is rapidly ‘scanning’ a new UI. In order to make this process simpler for assistive users is with Accessibility Headers. Think of these headers as summaries of sections, grouping functionality by components into logical groups.

 

 

Wow! If we look at the app again, this time with no regards to the fact that it is rectangular and separated into colourful functionality, how does it stand up as an experience? You can see that without the familiar layout we might struggle to group functionality quite so easily. The truth is that apps ship like this all the time and it is an incredibly frustrating experience for some users. These example accessibility descriptions might be over-simplified but it gives you an idea of how much we visually rely on layouts for context.

In the case of the calculator app if you use headers you would go from an endless list of buttons with no contextual logic, to a nice ‘Numbers, ‘Operators’ and ‘Output’ sections which rapidly help users navigate your app!

 

Swift UI Accessibility Headers

 

As with all things in SwiftUI adding functionality is as simple as adding a modifier. Lets start by defining our simple CalculatorView hierarchy:


1
2
3
4
5
6
// The digits to be read
CalculatorValueView()
// The numbers to use for calculations
CalculatorNumberView()
// The Operators such as plus and minus
CalculatorOperatorsView()

Now lets add header definitions for each resulting in the component’s name being read aloud for the user:


1
2
3
4
5
6
7
8
9
// The digits to be read
CalculatorValueView()
   .accessibility(addTraits: .isHeader)
// The numbers to use for calculations
CalculatorNumberView()
   .accessibility(addTraits: .isHeader)
// The Operators such as plus and minus
CalculatorOperatorsView()
   .accessibility(addTraits: .isHeader)

Now a user can quickly scan over the elements getting rapid context to quickly navigate your Calculator app. But what about hiding unnecessary things such as decorative images to declutter the UI of elements only there for aesthetic purposes? For this you can either use the dedicated `decorative` initialiser for Images or you can remove components completely using the `hidden` parameter for accessibility elements:


1
2
3
4
5
6
7
8
9
// Hiding elements completely
Text(verbatim: "Pointless UI message")
   .accessibility(hidden: true)

// Automatically declaring images as decorative and consequently hidden
Image(decorative: "PointlessLogo")
   .resizable()
   .frame(width: 40, height: 40)
   .accessibility(hidden: true)

Now your UI is decluttered from an Accessibility point of view and users can scan for things rapidly to speed up navigation.

Labels & Descriptions

In the calculator example there are a few things that we need to discuss: how would you read out the action which the ‘x’ and ‘-‘ do in the calculator app? We immediately see these symbols and translate them as ‘multiply’ and ‘subtract’ but this process isn’t quite as automatic for assistive users. Furthermore, what about the actual output of the calculation on the top? Perhaps you want to change the output from ‘1,984,242’ to ‘One million, nine hundred and eighty four thousand, two hundred and forty two’? Just improving these two things will take us closer to creating a better experience for all of our users.


1
2
3
4
// Generate a description from the current answer and read it out loud
CalculatorValueView()
   .accessibility(addTraits: .isHeader)
   .accessibility(value: Text("\($calculationModel.currentValue.wrappedValue.toStringDescription) degrees fahrenheit"))

The amazing thing about state-driven UI is that this value is automatically read out to the user each time it changes thanks to how SwiftUI monitors your models under the hood!

If you want to explicitly describe your component, short, sweet and descriptive labels can help:


1
2
3
4
5
// Explicit and descriptive labels when necessary
Section {
   MegaCalculationView(magicNumber: $model.incredibleNumber)
      .accessibility(label: Text(verbatim: "Perform mega calculation"))
}

The accessibility system reads your UI components’ accessibility description and thankfully when you create SwiftUI components you are defining a description label as part of the creation process. This is automatically used and read out to VoiceOver users to describe an element and its actions. For example with a standard picker:

1
2
// `Set the volume` is used to describe your Picker's action
Picker(selection: $volume, label: Text(verbatim: "Set the volume")) {

Here a user knows exactly what the picker does.

Accessibility Actions

Whenever your app has custom actions though, such as those triggered by gesture recognisers, you should have an accessibility action that can be performed without having to actually do the gesture on the screen. Accessibility users can trigger this action by selecting it through the VoiceOver interface without performing complex gestures that they may not find easy or even possible to perform.


1
2
3
4
5
6
7
8
   .accessibilityAction(named: Text(verbatim: "Erase all values")) {
   // You call your method that would usually be handled after a complex gesture
      self.resetValues()

   func resetValues() {
   // Reset value logic
   }
}

Conclusion

SwiftUI has enabled developers to reach an even wider-audience than ever before thanks to its incredible Accessibility features support. When designing apps Accessibility should be considered right at the foundational level. However even by adding some of the simple features above such as clear descriptions and logical groupings of components, you’re already well on your way to making your app more accessible than it was before. For more information check out the amazing WWDC2019 videos on Accessibility.

Come back for more SwiftUI findings and fun.