Advent of iOS Accessibility

Fri, Dec 6, 2024 7-minute read

Advent of iOS Accessibility

Starting something new: The Advent of iOS Accessibility. Twenty-four days of exploring some of the most common accessibility issues I’ve encountered, how to identify them, and—most importantly—how to fix them. Hope you enjoy the series!

Day 1

One of the accessibility issues I see more often in iOS apps, believe it or not, is unlabelled elements. This happens especially for buttons with an icon but no title. In those cases, you need to configure an accessibility label manually.

Calendar of the Advent of iOS Accessibility. Day 1. Buttons with no labels (especially buttons with an icon, but no title). Button with a bell, that intends to let the user see the notifications. If the button has no accessibility label, VoiceOver could say things like: button, bellIcon, possibly notification… far from ideal! A good accessibility label would be “Notifications”. In UIKit, you can just assign a localized string to the accessibilityLabel property for the button. In SwiftUI, you can use the accessibilityLabel modifier and pass in a Localized String Key.

Day 2

Some recommendations for improving your accessibility labels: don’t add the element type, avoid redundancy and verbosity, localize…

Calendar of the Advent of iOS Accessibility. Day 2. Better accessibility labels. A music player with some examples of how to improve your accessibility labels. Don’t add the element type: for the like button, if the label is “Like button”, VoiceOver will say “Like button, button”. So just “Like” is perfectly fine. Avoid redundancy and verbosity. For the repeat button, the label “Repeat This is the last time from Kean” would be too long, and if we specify the song and band for every action, it would be too verbose. Localize. For the next track button, try to use a localized string instead of a plain string so VoiceOver speaks in the correct language. Start with a capital letter and don’t end with a period. This helps with VoiceOver’s inflection.

Day 3

Anything representing a heading in the app should have the header trait. It allows for a faster way of exploring a screen and quickly jumping to the part of the app you are interested in. Screens should also start with a header.

Calendar of Advent of iOS Accessibility. Day 3. Missing header trait for headings. BBC News app shows two sections Technology and Science & Environment. These sections consist of a horizontally scrollable carrousel of at least 5 elements. If the titles of the sections have the header trait, a single swipe down brings you from one section to the other, compared to 6 swipes to the right needed otherwise. At the top of the app, there’s the BBC logo. Screens should start with a heading so the logo could also have the header trait. In UIKit you can insert the header accessibility trait to the accessibilityTraits property of the UI element representing the heading. In SwiftUI, you can use the accessibilityAddTraits modifier and pass isHeader as parameter.

Day 4

Important information is often conveyed visually through icons, badges, or progress bars… These details can easily be overlooked. Please make sure they’re part of your UI’s components' accessibility labels or values for a more inclusive experience.

Calendar of Advent of iOS Accessibility. Day 4. Visual cues missing from VoiceOver’s announcement. Four examples. The first one is a button with a person silhouette and a checkmark. VoiceOver announces it as “Collaborate, button”. It is missing the fact that you are already collaborating with someone. Represented by the checkmark. Second example shows a tab with the title “Coming soon” and a badge with the number 6. VoiceOver announces it as “Coming soon, Tab 2 of 5”. It is not conveying that there are 6 new movies and shows. Represented by the badge. Third example shows a chart’s title: “Daily Average 1h 55m”, an arrow pointing down, and “55% from last week”. VoiceOver says “Daily average, one hour fifty-five minutes. 55% from last week”. The user wouldn’t know if it went up or down. Represented by the arrow pointing down. Fourth example, episode download. The stop button is surrounded by circular progress bar. VoiceOver says “Stop downloads, button” but not how much is left.

Day 5

Images should either be decorative or have a proper accessibility label or alt text that describes them. If they’re decorative you can make it so they get skipped by assistive tech so it doesn’t get in the way of the experience.

Calendar of Advent of iOS Accessibility. Day 5. Images: with a description or decorative. iOS app with Disney+ showing the “How I met your mother” screen. The header has two images, one of them in the background, with the characters, that could be considered decorative. The other one is an image with the title of the series, which needs a description. In UIKit images are not accessible by default. So in the case of images that need to be accessible, you need to make the accessible elements. In SwiftUI images are by default accessible. You have an Image constructor with a decorative parameter, where you pass the name of the image.

Quick clarification on this one. It is probably not the best example of decorative images. I think these images should have alt text. But in my experience, most APIs won’t give you one for movie posters, music artwork, and things like that… And the point is that a random name just adds noise.

And another nuance. VoiceOver has a feature called Image Explorer. VoiceOver can recognise text, people and objects in images. So if you think that users can get value with this feature, you might want to consider exposing the image then.

Oh! And avoid images of text please!

Day 6

With regular buttons from UIKit or SwiftUI, you are all set. With complex views, headings, or table/collection view cells that, when selected, bring the user somewhere else in the app or perform an action, you’ll have to add the button accessibility trait yourself to convey users that it is an interactive component.

Calendar of Advent of iOS Accessibility. Day 6. Missing button accessibility trait. Four examples of not-so-obvious elements that require the button trait to be added by developers. First example, the collection view cells representing shortcuts in the Shortcuts app. Runs the shortcut when selected. Second example, the table view cell with a post in the Mastodon app. It opens the detail view and thread when selected. Third example, the What to Watch header in the Apple TV+ app. It can be selected and it will open the What to Watch screen. Fourth example, a view that is more complex than a regular button like a notification in Notification Centre that opens the notification.

Day 7

Grouping elements when it makes sense can make a huge impact on easing navigation with some assistive technologies like VoiceOver, Switch Control, or Full Keyboard Access. It also helps on reducing redundancy.

Calendar of Advent of iOS Accessibility. Day 7. Grouping elements for easier navigation. There is an example using the Foursquare app. There is a list of restaurants. Each restaurant has a name, type, location, and rate. By default, you’d need four swipes to the right with VoiceOver to go from one restaurant to the next one. That’s 32 swipes to go through 8 restaurants. If we group all these, it is just a single swipe to go from one item to the next one, easing navigation a lot. There is another example showing Next Door. The post in the example would require 9 swipes. So you can see how things can quickly get worse for more complex views. In that case, each post has a more options, like, reply, and share buttons that would repeat for every single item causing lots of redundancy.

In UIKit, this process consists of three steps:

  1. Setting the parent view as an accessible element: https://iosdev.space/@dadederk/109693895401281036
  2. Configure relevant accessibility label, value, traits and hints on the parent view: https://iosdev.space/@dadederk/109806337876803727
  3. If there are secondary actions within the grouped view, configure custom actions: https://iosdev.space/@dadederk/1097016

In SwiftUI, it might be as simple as using the .accessibilityElement(children:) modifier with the .combine accessibility child behaviour: https://iosdev.space/@dadederk/109932750048041110 If that doesn’t quite work as expected, try to tweak the internal views. If not, you can fall back to UIKit’s three step process.

Day 8

If a view has isAccessibilityElement to true, assistive tech won’t look for any of its subviews. That means that if there are any buttons inside, they won’t be accessible. You can add custom actions to be able to ‘interact’ with them.

Calendar of Advent of iOS Accessibility. Day 8. Inaccessible buttons within accessible views. Example of the Mastodon app. If we make the table view cell representing a post an accessible element so it is easier to navigate, the subviews, including the buttons, won’t be accessible anymore. So how can a VoiceOver, Keyboard, Voice Control, or Switch Control user interact with them? Custom actions to the rescue. In SwiftUI there is a new modifier, since iOS 16, called accessibilityActions. Before, you had to add the actions one by one. In UIKit, there is an accessibilityCustomActions property, which is an array of UIAccessibilityCustomAction.

Day 9

If you have interactions that are hidden or require complex gestures to be performed or that may conflict with VoiceOver, you need to provide alternative ways of executing these actions. Custom actions can help a lot of times, but not always.

Calendar of Advent of iOS Accessibility. Day 9. Hidden actions not being accessible. Find alternative ways of actioning hidden interactions or that require complex gestures or gestures that conflict with VoiceOver’s. Two examples. The first one is swiping left in a table view cell to unveil a delete option. That could be fixed by adding delete as a custom action. Second example is a bottom sheet menu with a grid of options. You need to swipe down to dismiss it. You’d probably need to implement the escape gesture for VoiceOver and it would be a good idea to add a close button anyway.

Day 10

Toggles or UISwitches are often found separated from the label that precedes (and describes) them; with an unclear label; missing a value, trait, or hint; or even not being actionable at all.

Calendar of Advent of iOS Accessibility. Day 10. Bad experience with toggles. Very often toggles or UISwitches are not grouped together with the label preceding them or lack of the right values, traits and hints. Three examples. With the first one VoiceOver just says “Shared accomodation” and it is not even actionable. The second one focuses first on the label and then on the switch. VoiceOver says “Event cancellations” and then “Switch button, on, doublet-tap to toggle setting”. The third one does a similar thing but treats the switch as a button. VoiceOver says: “Like” and then “Button”. All quite confusing. With UIKit you can configure your UISwitch as the accessoryView for a table view cell. With SwiftUI you can use a named Toggle that lets you specify an associated label.

Day 11

Have you ever seen VoiceOver randomly focusing on elements of the previous view when presenting a custom modal view? That can be fixed by letting the system know that the presented view is modal in terms of accessibility.

Calendar of Advent of iOS Accessibility. Day 11. Confusing custom modals. There is a groceries app. It has some text that says “Sorry, we don’t deliver there yet” a search field, a search button, a text that says “or” and a login button. When presenting a modal view on top of it, VoiceOver still focuses through the elements of the previous screen. To avoid that with UIKit, you can set accessibilityViewIsModal to true for the modal view. With SwiftUI there is an accessibility trait for that, .isModal.

Day 12

Sometimes we may fail to update users of things changing on the screen in a perceivable way. Toasts and similar should be announced. We may want to make clear that some content on the screen changed or to update on a task’s progress.

Calendar of Advent of iOS Accessibility. Day 12. Failing to update the user of changes. Three examples over the Apple TV app on the Ted Lasso page. The first one has a hud saying “Episode added to Up Next”. Toasts, snackbars and huds can be conveyed to VoiceOver usersa using accessibility announcements. The second one is a drop down to select the season which changes the episodes visible in the carrousel underneath it. A layout changed notification can help with changing content in the screen. The third one shows an episode with a download indicator. The updates frequently trait could help with that.

Day 13

Sometimes, you may want to create a custom component, even if there is a similar one in UIKit or SwiftUI because you want to style it in a way that the default one won’t let you. Or add extra functionality. That’s fine, but please take into account that you may need a bit of extra work to make it accessible.

Calendar of Advent of iOS Accessibility. Day 13. Custom components can need a bit of work. If you develop a custom tab bar, you need to set the tab bar accessibility trait in the container, then you need to set up the container type with the value semantic group, then give it a localised string, “Tab bar”, as an accessibility label. Also, you need to set up the large content viewer by setting show large content viewer to true and adding the interaction to the buttons… If you use Swift UI, the .accessibilityRepresentation(representation: ) modifier can help you create more accessible custom components.

Day 14

iOS and Xcode provide a wide variety of tools and options to deal with color, and help us providing good color contrast ratios. From system colors that automatically support Increase Contrast, to high contrast (and light and dark mode) color asset variants, automatic checks with the Audit feature in the Accessibility Inspector, and even a built-in contrast calculator.

Calendar of Advent of iOS Accessibility. Day 14. Color contrast. Some of the tools available in Xcode that help you create apps with great contrast are the asset catalog which lets you provide high contrast variants for color sets and assets, and the color contrast calculator. Some examples show how text size also affects contrast ratios. White on Black passes for all sizes. A grey with a 4.5 to 1 ratio over black will fail on small text sizes. A grey with a 2.9 to 1 ratio over black, fails for all text sizes. You can also use system colors an make sure that your app works well with the High Contrast setting.

Day 15

Touch target sizes are recommended to be at least 44 x 44 points for better usability. Buttons in the navigation bar (especially when not using nav bar button items), dismiss buttons, and custom toolbars, are common examples that often fall below this size.

Calendar of Advent of iOS Accessibility. Day 15. Small touch target sizes. Three examples of common cases with buttons that tend to have small touch target areas. The first one is buttons in the navigation bar. The second one is dismiss buttons for modal or inline popups. The third one is toolbars, especially custom ones.

Day 16

A reminder that the more modes we use to convey important information, the more likely it is that all users will perceive it. Consider a combination. of color, icons, messages, sound, haptics, animations, etc.

Calendar of Advent of iOS Accessibility. Day 16. Multimodal information. One example shows an app where you need to introduce a 6-digit pin. When it is the wrong pin, it does a shake animation on the pin field. It is using one mode: animation. If reduce motion is enabled, or for VoiceOver users, this information will not be perceived. The second example shows the same app but it adds a warning icon and a message that says “Incorrect pin” in red and the device also vibrates and does a sound to indicate the error. It uses several modes: animation, iconography, messaging, color, sound, and haptics. This is preferred.

Day 17

Check the traversal order of elements in your app. Sometimes the default top-left to bottom-right order might not be the most logical one. Sometimes you may consciously want to tweak the order. Other times, grouping is the answer.

Three examples of traversal order for three pieces of data in three columns. The first one says Followers and 550 underneath, the second one Following with 340 underneath, and the third one Posts with 750 underneath. In the first one, the order is: Followers, Following, Post, 550, 340, 750. This order is incoherent. For the second one, the order is: Followers, 550, Following, 340, Posts, 750. This one has a logical order. The third one’s order is: Followers 550, Following 340, Posts 750. Where both name and number for each one of the pieces of data is grouped. It is clearer and the navigation is easier.

If you want to find out more, we had an interesting conversation in Bluesky with Paul J. Adam, on the nuances of how to implement this.

Day 18

When building custom components, or if not relying on UIControl’s attributes to configure state, it can be easy to forget to specify the right accessibility traits. These are indispensable for a good experience with VoiceOver, Switch Control, Voice Control, Full Keyboard Access…

Calendar of advent of iOS Accessibility. Day 18. Missing traits. Three examples. The first one shows a custom segmented controller. These tend to have a missing selected trait for the selected option. The second one shows a custom page control. These should configure an adjustable trait. The third one shows a button that shows the disabled state by changing the color of the text instead of setting isEnabled to false. This means the button will be lacking the not enabled trait.

Day 19

Accessibility labels might not be the best input labels, used for example to find or interact with elements with Voice Control or Full Keyboard Access. In those cases, you can provide accessibility user input labels.

Calendar of Advent of iOS Accessibility. Day 19. User input labels. One example shows Apple’s Podcast app in the 13 letters podcast page. There is a button in the top right corner with a + icon. The default label is “Follow”. That might not be everyone’s first guess on how that button is named. You could add “Plus” and “Add” as alternative labels. The second example is some episodes of that same podcast. The default label is very long consisting of a few details about each episode like the publication date and title. That might be a bit long for Voice Control. You can use just the title, or the word “Episode” as possible input alternatives.

To know more about accessibilityUserInputLabels checkout: “Improving Accessibility: Voice Control” by Bas Broek

Day 20

There is an option for the user to request an experience with Reduce Motion, and we should honor it. If your app has animations, make sure to check if the user has this setting on. Here are three examples where Apple does a great job.

Calendar of Advent of iOS Accessibility. Day 20. Reduce Motion. Three examples. In the App Store app, Apple sometimes has tiles with icons from apps or games that continuously moves for the user to get a sneak peek of all the icons. The second one is the Weather app, where there are quite realistic animations of the weather conditions in the background. The third one is the Stocks app. If you open one of your symbols, there is a banner at the top where your symbols keep automatically scrolling mimicking what you see on tv. In all these three cases, Apple completely stops the animations if Reduce Motion is on.

You can support Reduce Motion in UIKit and SwiftUI.

Day 21

There are a few accessibility settings you can check for, or get notifications in case these preferences change. This is especially important when developing custom components as they will mostly work with UIKit and SwiftUI controls.

Calendar of Advent of iOS Accessibility. Day 21. Honoring users' settings. Display & Text Size Accessibility Settings Screen. There are two examples, Apple’s podcast app playing an episode of Swift by Sundell and FaceTime. There are arrows pointing from some of these settings to how they take effect in these apps. When Reduce Transparency is on, in FaceTime you don’t see the front camera being previewed and blurred in the background, instead, it is just opaque. When bold text is on, all text in both apps is bold. When Increase Contrast is on, the New FaceTime button is darker. When Button Shapes is on, the button for the podcast show or the playback speed (both consisting of just text with the app’s tint color) is underlined so it is easier to be identified as buttons. The sleep timer button, which is an icon, gets some borders around it too. When smart invert is on, the artwork of the podcast doesn’t get inverted.

Checkout UIKit’s Accessibility Capabilities and SwiftUI’s Accessibility environment variables.

Day 22

Make sure you support Dynamic Type up to the largest text size available. Take into account that there are five extra accessibility sizes available from the Accessibility Settings. It can make a huge difference for lots of users.

Calendar of Advent of iOS Accessibility. Day 22. Dynamic Type Support. The Mastodon app in two sizes, the default one (large) and the fifth accessibility text size (the largest). Some things you can do to support dynamic type: support it even when using custom fonts, save asset’s vectorial data, scale icons and buttons too, adjust sizes automatically, tweak the layout if necessary, implement large content viewer. You can test in the SwiftUI variants preview, with the accessibility inspector, in the simulator, using environment overrides…

Day 23

Sometimes your UI will just not optimally scale for large text sizes. Simple changes, for accessibility sizes, like composing elements vertically instead of horizontally, reducing the number of columns, and allowing more lines of text, can do the trick most times.

Calendar of Advent iOS Accessibility. Day 23. Adapt your UI. Example of Apple’s Stock app. The first one has the default text size. The second one uses the largest possible text size. In the first one, the symbol and name are at the left of its row, there is a small graph in the middle, and the value and percentage change to the right. In the second one, the symbol, name, value, and percentage change are at the left, taking most of the row width, and the only thing to the right is the graph. That gives plenty more horizontal space for the text.

Day 24

Test manually. Familiarise yourself with different assistive technologies. I find it useful to start with VoiceOver but check out Voice Control, Full Keyboard Access, and others… Remove friction, configuring shortcuts can help. Happy festive season everyone!

Calendar of Advent of iOS Accessibility. Day 24. Test Manually. Go to Settings, Accessibility, scroll to the bottom, and tap Accessibility Shortcut. Select the accessibility features you’d like in the accessibility shortcuts menu. VoiceOver, Switch Control, and Voice Control are selected in the example. Now triple-clicking the side button on your iPhone (or the home button if you still have one) will show an action sheet with the features selected previously.

Happy festive season everyone!

Thanks for following the series till the end! For more accessibile iOS apps in 2025!

Popular logo used for representing accessibility consisting of a stick man inside a circle. But the stick man is dressed as Santa Claus and there is some text around it that says “Accessibility. Have you been naughty or nice?”.

You can also follow the series in: