UX Collective

We believe designers are thinkers as much as they are makers. https://linktr.ee/uxc

Follow publication

Making your iOS apps inclusive with UIKit and SwiftUI accessibility

--

APP ACCESSIBILITY

Pretty much everyone uses smartphone apps, including people with disabilities. Building accessible apps help them use them. So let’s create apps that leverage the power and simplicity of iOS’ assistive technologies and adapt our apps to make them accessible to all, regardless of their physical or learning abilities, and by structuring their interface for accessibility accordingly.

DISABILITY INCLUSION

One billion people, or 15% of the world’s population, experience some form of disability. Barriers to full social and economic inclusion of persons with disabilities include the unavailability of assistive devices, technologies, and apps. Global awareness of disability-inclusive development, however, is increasing. The World Bank has some useful stats for this.

APP ACCESSIBILITY MYTHS

Maybe during the development of your own apps, you have come across excuses like these (I have certainly been guilty of this):

  • Seems like a lot of work for a small portion of your user base.
  • “There is no way I’ll be able to convince my partners/bosses/stakeholders”.
  • It’s too hard.
  • Deadlines, budgets, office politics, and all that nonsense*.

Unfortunately for everyone, accessibility is often at the bottom, or not even in, the priority list. Why is that? It assuredly should not be and we, as app builders, can definitely do better than that.

*You know what I mean 🙂

HOW ACCESSIBLE IS YOUR APP NOW?

iOS Accessibility settings menu

In the Settings app under Accessibility ▸ Accessibility Shortcut, you can access voice-over on-demand with a power button triple-tap.

You can run your own apps now to see how they behave.

VoiceOver on iOS within an app

HOW TO MAKE YOUR IOS APP MORE ACCESSIBLE WITH UIACCESSIBILITY (THE EASY STUFF)

Make sure all elements have accessibility labels via the accessibilityLabel property.

💡 You don’t need to add “Button” etc. because iOS will automatically add that for you.

💡 If you don’t provide an accessibility label for images, VoiceOver will use whatever name you gave your image filenames; so don’t be too creative there.

You can set the accessibilityHint property to be read right after the label, the first time it is focused upon.

💡 Ending with a full stop or period helps VoiceOver read the hint more naturally.

💡 If you localize your app, make sure you localize your accessibility labels and hints.

The accessibilityTraits property lets you describe what functionality your components have — if they are buttons, links, or images, and so on.

/*
╔═════════════════════════╦═══════════════════════════════════╗
TraitDescription
╠═════════════════════════╬═══════════════════════════════════╣
║ button ║ Tappable button element ║
║ link ║ A tappable website link ║
║ searchField ║ Search field ║
║ image ║ A graphic ║
║ playsSound ║ Plays a sound ║
║ keyboardKey ║ A keyboard key ║
║ staticText ║ Immutable Text ║
║ summaryElement ║ Summary, read once ║
║ notEnabled ║ Element is not enabled ║
║ updatesFrequently ║ Element updates oftentime ║
║ startsMediaSession ║ VoiceOver won't interrupt media ║
║ adjustable ║ Allows adjustments like swipe ║
║ allowsDirectInteraction ║ VoiceOver is overriden ║
║ causesPageTurn ║ Element causes a page turn ║
║ header ║ Divides content into sections ║
╚═════════════════════════╩═══════════════════════════════════╝
*/cell.accessibilityTraits |= UIAccessibilityTraitButtoncell.isAccessibilityElement = falsecell.contentView.subviews.forEach { $0.isAccessibilityElement = true }

Ensuring every UI element has a label, hint and trait is a pretty good start.

It’s pretty good!

The accessibilityUserInputLabels property is new to iOS 13 and applies specifically to Voice Control. By default Voice Control will use the accessibilityLabel; Using this optional array of Strings, however, allows us to specify shorter, and perhaps easier to consume, variations thereof.

addButton.accessibilityUserInputLabels = [
NSLocalizedString("New", comment: ""),
NSLocalizedString("Create Post", comment: ")
]

The accessibilityViewIsModal is a boolean that should be set on a view that is presented modally. By setting this property, VoiceOver will ignore all elements that are outside the view’s hierarchy.

modalViewController.view.accessibilityViewIsModal = true
modalViewController.modalPresentationStyle = .overCurrentContext
present(modalViewController, animated: true, completion: nil)

INTERLUDE: DYNAMIC TYPE TO RESIZE YOUR APP’S TEXT

Users can set a system-wide preferred font size for all apps.

// headlinetextView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.headline)// subheadlinetextView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.subheadline)// bodytextView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)// footnotetextView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.footnote)// caption 1textView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.caption1)// caption 2textView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.caption2)

💡 You can use NotificationCenter to subscribe to the UIContentSizeCategoryDidChange to refresh your user interface if you receive it.

THINGS TO KEEP IN MIND

UIKit is accessible out of the box. So, UILabel, UIButton, UISlider, etc. are all highly accessible for free.

On UIView subclasses ensure you set isAccessibilityElement to true so it's accessible, and set accessibilityValue property to something that VoiceOver can read.

VoiceOver reads exactly what is on the screen; so if your app says 37km, make sure VoiceOver says “thirty seven kilometers”.

distanceLabel.text = "37km"
distanceLabel.accessibilityValue = "thirty seven kilometers"

TIPS

For extra ❤️, you can customize your UIs for VoiceOver. Check whether VoiceOver is running by calling UIAccessibilityIsVoiceOverRunning(), and adapt your UI accordingly. E.g. you can limit animations, elements that disappear after a second or two can stay for longer, or show additional UI.

If you opt to make your app accessible, integrate accessibility into your workflow. So if your UI changes, update the accessibility values and functionality.

SWIFTUI ACCESSIBILITY

Set an accessibility label on your SwiftUI element using the modifier .accessibility(label: Text(“Done”))

Button(action: {},       label: { Text(“➡️✉️”) })  .accessibility(label: Text(“Send”))
Slider(value: $sliderValue, in: minimumValue…maximumvalue) .accessibility(value: Text(“”\(Int(sliderValue)) out of 10"))

This should be the text representation of the value or content of a control.

Button(action: {}, label: { Text(“➡️✉️”) })   .accessibility(hint: Text(“Sends your message.”))

You can cater to users that have the Reduced Motion setting active thus:

// Get the value from the environment
@Environment(\.accessibilityReduceMotion) var reduceMotion
Circle()
.frame(width: 20.0, height: 20.0)
.scaleEffect(0.5)
.animation(reduceMotion ? nil : .easeInOut)

SwiftUI uses the names of your images as accessibility labels, which is helpful. However, certain images shouldn’t be read because they are “decorative” (maybe they are there just to make the UI beautiful). Using:

Image(decorative: "my-image")

tells iOS VoiceOver to skip reading this.

CUSTOM ACTIONS

If the above examples didn’t cover all your use cases you can also implement Custom Actions. They’re shortcuts to common operations and have been available since iOS 8.

VoiceOver will declare when a view has custom accessibility actions. You can iterate between them by swiping; and a double-tap will activate them. This saves time because users will not have to go through all the elements of a view to find actions.

myView.accessibilityCustomActions = [  UIAccessibilityCustomAction(name: "One"), 
actionHandler: { [weak self] in
// Do something for One
})

UIAccessibilityCustomAction(name: "Two"),
actionHandler: { [weak self] in
// Do something for Two
})
UIAccessibilityCustomAction(name: "Three"),
actionHandler: { [weak self] in
// Do something for Three
})
]

IN CONCLUSION

As responsible developers, with empathy towards others, we want everyone to just be able to use our apps. It's also part of the job to put ourselves into other people's shoes. The technology outlined here is a good step towards that direction. If you are just getting started, making your app fully accessible might seem like a monumental task but once the initial implementation is built; then implementing accessibility features (even just a few of the above) for new functionality is much easier, manageable, and rewarding because someone's user experience just became a little bit easier.

RESOURCES

WWDC Accessibility videos https://developer.apple.com/videos/frameworks/accessibility

Accessibility on iOS https://developer.apple.com/accessibility/ios/

Ray Wenderlich iOS Accessibility: Getting Started https://www.raywenderlich.com/6827616-ios-accessibility-getting-started

💯 DEVit 2017: Technologic (Human Afterall): Accessibility mix https://www.youtube.com/watch?v=oeDzH3nskbg

The UX Collective donates US$1 for each article published in our platform. This story contributed to Bay Area Black Designers: a professional development community for Black people who are digital designers and researchers in the San Francisco Bay Area. By joining together in community, members share inspiration, connection, peer mentorship, professional development, resources, feedback, support, and resilience. Silence against systemic racism is not an option. Build the design community you believe in.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Written by Dimitri James Tsiflitzis

Developer, scrum enthusiast, @sequeapp building productivity apps; formerly @nimber, @peopleperhour, @taxibeat; game dev @sprimp and orgniser @cocoaheadsgr

No responses yet

Write a response