Accessibility in iOS: Hands on Code
So, we know enough about accessibility as per our last discussion.
We will be creating a contact list application for making hands on code. We will use MVVM architecture and and API based data for populating a UITableView using Swift in iOS.
Before we can go ahead for implementation in code, need to discuss about accessibility attributes.
Accessibility Attributes
Accessibility attributes help assistive application to know how an accessibility element behaves or should be treated by setting these.
These attributes provide information from application to VoiceOver, can be announce to user. If these attributes don’t have proper information then VoiceOver will not announce as expected.
There are some accessibility attributes as:
- isAccessibilityElement: a bool value decides wether element should be exposed as an accessibility element or not. Default is
false
. - AccessibilityLabel: a string to summarily identify the control or view, but shouldn’t include type. For example: label for back button should be
Back
notBack Button
. - AccessibilityTraits: describes aspect of element’s usage, state or behaviour. Some traits may be combine (by an OR operation) as per requirement. For example: button may have
.button
or.button, .selected
traits. - AccessibilityHints: describes the result of performing an action on element. For example: a favourite button may have
makes favourite
orremoves favourite
notDouble tap to make favourite
. - AccessibilityValue: describes the current value of element. For example: a textField will have same value as
text
property.
These are the essential properties for implementing the accessibility in application.
📣 Most of the UIKit elements already set these attributes for you, just need to supply details for improve user experience.
We will be using standard UIKit views and controls those the already accessible.
Let’s start with implementation
So, our contact list application will fetch data from API and store data in view model, data will be populated in view controller. And will be having feature of making favourite and unfavourite. Let’s do this.
- Create an project named
AccessibilityDemo
and setup the folder structure as 👇:
2. Create a structure ContactInfo
as model to show in above folder structure.
3. Create another structure ContactListServerResponse
as model for handling API response.
4. Create a class ContactListViewModel.swift
that will be attached with view controller. view model will be having array of ContactInfo
, a method for fetching data from server making rest API call, a methods for updating fav contact.
5. Create view controller ContactListViewController.swift
having a table view data for table view will be populate from view model. Having a loading indicator and function for loading and stoping loader.
Download the project till now form here.
Working with accessibility attributes:
Now, run the application and let’s test the accessibility. And open accessibility inspector, start inspecting elements one by one. Like 👇
Now, we can see inspector focusing complete cell as one element and setting accessibilityLabel with comma separated text, that is text of both labels.
Favourite button is focusable as different, having ic un fav
as label . It means image name from assets set as accessibilityLabel (we need to change it). Trait is .button
for unselected and .button, .selected
for selected one.
To execute action on any button you can click on Perform in Actions section.
Now, firstly make elements accessible individually (in some cases be need it).
Open ContactListTableViewCell.swift
file and add the below mentioned code. 👇
Line #42: enabling image view to be accessible by VoiceOver.
Line #45–54: overriding default implementation of accessible elements for cell.
These elements will be focusable as the same order added in array. For default behaviour of accessibilityElements
VoiceOver focuses elements as the hierarchy in xibs or storyboards. VoiceOver reads them from top to bottom in hierarchy.
📣 Define your view/elements hierarchy in xibs and storyboard carefully as this will be default implementation for `accessibilityElements`.
Now, add below mentioned function in same file, and make a call for this function in func configure(withInfo info: ContactInfo)
at last. 👇
Line #68: providing accessibilityLabel string to be announce when focus is on image view.
Line #70: replace default accessibilityLabel from ic un fav
to favourite
for favourite button.
Line #71: providing accessibilityHints as per the button’s selected state.
Now, again run and check the attributes got updated as we defined.
Working with audit warnings and fixing:
Now, go to audit (⚠️) section in accessibility inspector. Here we saw a Run Audit button, as you click on this button accessibility inspector will run an audit on screen which is presenting in simulator.
📋 Don’t forget to select simulator in target of accessibility inspector.
After successfully executing audit of screen you may see some warnings like 👇
Here we can see two types of warnings, let’s discuss and resolve them.
1. Hit area is too small: our favourite button having 25x25
size as per Apple UI/UX guidelines any button must have area of min 44x44
.
So, to resolve this warning just one ContactListTableViewCell.xib
and change button height from 25 to 45, width will be changed automatically as our button having hight width ration of 1:1
2. Dynamic Text font sizes are unsupported: it’s warning about, when user will change text size from settings application of iPhone from “Settings app → Accessibility → Display & Text Size → Large Text” and change the value of slider. OR you can change it by going on to settings (⚙️)option of accessibility inspector and change the slider’s value.
So, to resolve this we need to make our label’s font to be adjustable as per content size category. We can set it either in xib by choosing textStyles or programmatically setting textStyles, doing it programmatically copy below mentioned code and paste at last in earlier created applyAccessibility()
method.
self.nameLabel.adjustsFontForContentSizeCategory = true
self.infoLabel.adjustsFontForContentSizeCategory = trueself.nameLabel.font = UIFont.preferredFont(forTextStyle: .title3)
self.infoLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
Now, run application and perform Run Audit
booooom! (not a magic 😋) all warnings gone.
Let’s test the text size increasing and decreasing functionality 👇.
Okay, so we just used textStyles to achieve text size resizing, what if we need to use our custom fonts.
for using our custom fonts we need to scale our font according to the current contact size category by using font metrics (seems too much confusing 😄). Take a look on below code, paste at last in ContactListTableViewCell.swift
extension UIFont {
/// Scaled and styled version of any custom Font
///
/// - Parameters:
/// - name: Name of the Font
/// - textSize: text szie i.e 10, 15, 20, ...
/// - Returns: The scaled custom Font version with the given size static func scaledFont(name: String, textSize size: CGFloat) -> UIFont {
guard let customFont = UIFont(name: name, size: size) else {
fatalError("Failed to load the \(name) font.")
}
return UIFontMetrics.default.scaledFont(for: customFont)
}
}
Now, we can use this code to providing our custom font. Copy below code and replace last two lines in applyAccessibility()
method.
self.nameLabel.font = UIFont.scaledFont(name: "HelveticaNeue-Bold", textSize: 15.0)self.infoLabel.font = UIFont.scaledFont(name: "HelveticaNeue", textSize: 12.0)
Now, again run application and try changing text size. It’ll work smoothly.
You can use any other font name as you want.
To resize cell height you’ll need to return ‘UITableView.automaticDimension’ in heightForRowAt indexPath
method.
Download final project with all changes.
Tips and Tricks
- UIButton frame never resize while changing content size category, you need to do it manually but reseting constraints of titleLabel with UIButton. I’ve created a CustomButton class to achieve this. Like 👇
Now, add two buttons on a view, provide font
as textStyle
of Caption 1
. And assign this class to any one of them.
Run, the code and try to change the text size. You’ll observe our CustomButton
getting reframed as content size category is changing.
2. Accessibility Notifications: when any screen changes or getting updated in your application, you’ll need to refocus any specific element than accessibility notification comes in to scene. we need to post a notification that with argument of notification type and element that will be focused. Like:
UIAccessibility.post(notification: .screenChanged, argument: <element to be focused>)
there are different types of notifications i.e. screenChanged, layoutChanged, announcement, pageScrolled, pauseAssistiveTechnology, resumeAssistiveTechnology
use as per your requirement.
3. UITableViewHeaderFooterView
’s first subview will be always having .header
trait, you can not remove it.
4. Don’t be afraid to make custom UI, you can check anytime if VoiceOver is running or not by calling UIAccessibility.isVoiceOverRunning, modify different UI as needed.
5. Last but not least, VoiceOver always reads what it sees on the screen by default, but until you actually didn’t tell it what to announce i.e. 21 km
it will announce same as it is, you need to set accessibilityLabel
as 21 kilometres away
to be announced.
So, let’s end the discussion as all the needed things have been covered.
Now, we are able to implement accessibility in any application. But don’t stop here, as there are lots of things to explore in accessibility.
👩💻 !!! HAPPY CODING !!! 👨💻
Thank you for reading, please hit the recommend icon if like this collection 😊 . Questions or Doubts? Leave them in the comment, let’s discuss more on them.