iOS developer guide

This guide aims to present the various iOS SDK accessibility options. Through different categories, this guide explains how to use the accessibility attributes / methods and provides links to the official documentation from Apple. Code snippets are also available to show you how to implement it (mostly in Swift).

Text alternatives

Description:

On iOS, the vocalization of an element is done through four attributes: label, hint, value and trait. The order of vocalization is always as follows: label, value, trait and hint. This order cannot be changed and the vocalization is performed only once.

A section of this guide is dedicated to the trait, we describe here the other three:

  • AccessibilityLabel: the label redefines the text read by VoiceOver. This allows a component to be more explicit than the text displayed on the screen. For example, for a button whose title is “OK”, this attribute can indicate that the button is used to confirm an action.
  • AccessibilityValue: the value of an element is by default the completion percentage (e.g. a progress bar percentage). Note that for most elements available in the SDK, this value does not need to be set (the system automatically sets the value).
  • AccessibilityHint: thehint describes the component’s behaviour. Example: “click here to get the result”.

These accessibility attributes are available via the builder interface but also programmatically. Anything inheriting from UIView has these attributes by default. These attributes accept an optional string, and are therefore easily localizable.

Examples:

Simple example (localized):
accordionHeaderView.accessibilityHint = "example_elementState_foldArea_open_accessibilityHint".localized

Example of custom tabs indicating their trait and state:

@IBAction func buttonClicked(sender: AnyObject) {
    for button:UIButton in buttonList {

        if button == sender as! UIButton {
            button.selected = true
            if accessible {
                button.accessibilityTraits = UIAccessibilityTraitButton + UIAccessibilityTraitSelected
            }
            button.setTitleColor(UIColor.orange_orangeForWhiteBG(), forState: UIControlState.Selected)
            button.tintColor = UIColor.clearColor()
        }
        else {
            button.selected = false
            if accessible {
                button.accessibilityTraits = UIAccessibilityTraitNone
                button.accessibilityTraits = UIAccessibilityTraitButton
            }
            button.setTitleColor(UIColor.orange_blackColor(), forState: UIControlState.Normal)
        }
    }
}

func accessibleSegmentedControl() {
    onePageButton.accessibilityTraits = UIAccessibilityTraitButton + UIAccessibilityTraitSelected
    twoPageButton.accessibilityTraits = UIAccessibilityTraitButton
    threePageButton.accessibilityTraits = UIAccessibilityTraitButton

    onePageButton.accessibilityHint = "1 " + "common_of".localized + " " + String(buttonList.count)
    twoPageButton.accessibilityHint = "2 " + "common_of".localized + " " + String(buttonList.count)
    threePageButton.accessibilityHint = "3 " + "common_of".localized + " " + String(buttonList.count)
}

Links:

Element trait

Description:

The accessibilityTraits attribute allows to specify the trait of an element to the accessibility API. Thus, it is possible to make a list item be considered as a button because it is clickable. Therefore, the accessibilityTrait attribute plays an important role on the element vocalization because the trait is vocalized by VoiceOver.

This accessibility attribute is available via the builder interface but also programmatically.

There are many available traits. The more commonly used are:

  • AccessibilityTraitNone: removes any semantic value to the element.
  • AccessibilityTraitButton: adds the “button” trait, the element is seen as a button by VoiceOver.
  • AccessibilityTraitLink: useful to define a label as a “link”.
  • AccessibilityTraitHeader: defines an element as a header (for more information, see the “Titles and headers” section).
  • AccessibilityTraitAdjustable: defines an element as an “adjustable” element, that is to say an element that users can adjust in a continuous manner, such as a slider or a picker view.

Examples:

Example with a UIPageControl and the “adjustable” trait:
pageControl.accessibilityTraits = UIAccessibilityTraitAdjustable

Example with a header trait :
defaultHeaderViewCell.accessibilityTraits = UIAccessibilityTraitHeader

We can also combine traits:
onePageButton.accessibilityTraits = UIAccessibilityTraitButton + UIAccessibilityTraitSelected

Link:

Hide elements from accessibility

Description:

It is possible via an accessibility attribute to hide elements from accessibility tools (e.g. VoiceOver). By extension, it is possible to force some elements to be visible to accessibility tools.

  • AccessibilityElement: boolean to specify that an element is visible or not to the Accessibility API (VoiceOver or other).
  • AccessibilityElementIsHidden: boolean to indicate that the children elements of the target element are visible or not to the Accessibility API.
  • AccessibilityViewIsModal: boolean that can make visible or not the sibling elements of the target element to the Accessibility API. Very useful for making accessible custom popins for example.

The accessibilityElement attribute is available via the interface builder but can also be used directly through the code. The other two attributes are available only through the code.

Examples:

Simple example:
pageControl.isAccessibilityElement = true

Example of an accessible custom alert:

@IBAction func displayCustomAlert() {
    popUpVIew.hidden = false //The view has been created before, we only make it visible and accessible
    popUpVIew.accessibilityViewIsModal = true //Prevent VoiceOver seeing what is behind the popin
    popUpVIew.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height) //The modal filter of the alert covers the hole screen

    let window = UIApplication.sharedApplication().keyWindow
    window?.addSubview(popUpVIew)

    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, popUpVIew) //We notify that a popin is shown in order to be focused by VoiceOver, if enabled
}

Links:

Trigger a vocalization

Description:

It is very easy to trigger vocalizations with VoiceOver. Note that we are talking about VoiceOver vocalization and not TTS (Text To Speech) that can operate whether VoiceOver is on or off.

To trigger a vocalization, just call the UIAccessibilityPostNotification method passing the notification allowing to trigger a vocalization (UIAccessibilityAnnouncementNotification) and the string to vocalize as parameters.

Note: the vocalization is done in the system’s language.

Example:

UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, speakString);

Links:

Check accessibility options state

Description:

On iOS, it is possible to check the accessibility options state. Is VoiceOver activated? Is the audio-mono mode activated? Several methods can help you to check with that. They are part of the UIKit framework.

The most useful method is UIAccessibilityIsVoiceOverRunning which allows to know whether VoiceOver is activated.

Exemple:

UIAccessibilityIsVoiceOverRunning() ? 1 : 0

Links:

Notify a content change

Description:

When there is a content change in the current page, it is possible to notify the accessibility API using several types of notifications. To do that, we must send the change notification to the accessibility API using the following method: UIAccessibilityPostNotification.

There are several types of change notifications but the two most commonly used are:

  • UIAccessibilityLayoutChangedNotification: notifies that a part of the page has changed.
  • UIAccessibilityScreenChangedNotification : notifies that the whole page has changed.

There is a small difference between them. We can pass a NSString or a UIObject to the UIAccessibilityLayoutChangedNotification method. With a NSString it behaves like a UIAccessibilityAnnouncementNotification and triggers a VoiceOver vocalization. With a UIObject the focus is set on the UIObject.

The parameter for UIAccessibilityScreenChangedNotification can be either nil or the elements to set the focus on.

Examples:

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, @"speakString");
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, aViewObject);

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, aViewObject);

Links:

Change the vocalization language

Description:

To change the vocalization language of VoiceOver for a word or a sentence, one can use the accessibilityLanguage attribute. Available through UIAccessibility, this attributes allows to specify a language for a specific text. E.g. if we use this attribute on a UILabel, it will be vocalized by VoiceOver in the language set on this attribute.

Example:

cell.textLabel?.accessibilityLanguage = "fr_FR"

Link:

Modify the focus area of VoiceOver

Description:

In the case of dynamically modified element or component not inheriting from UIView, it is possible to modify the focus area of accessibility of this element, i.e. the area VoiceOver highlights when focusing an element.

  • AccessibilityFrame: sets the area via a rectangle (CGRect). Generally, for an element inheriting from UIView, this area is the “visible” part of the view.
  • AccessibilityPath: equivalent to AccessibilityFrame but sets the area via Bezier curves.
  • AccessibilityActivationPoint: the activation “point” of an element for accessibility tools. By default, this point is at the centre of the element.

Links:

Grouping elements

Description:

The shouldGroupAccessibilityChildren attribute is a Boolean that indicates whether VoiceOver must group its children views. This allows making unique vocalizations or define a particular VoiceOver reading order for a part of the page (see “Reading order” section).

Example:

Very useful when we want to customize the VoiceOver reading order, like here on a table cell containing form data:

class AddressTableViewCell: UITableViewCell {

    @IBOutlet weak var deliveringAddressLabel:  UILabel!
    @IBOutlet weak var addressLabel:            UILabel!
    @IBOutlet weak var switchLabel:             UILabel!
    @IBOutlet weak var addressSwitch:           UISwitch!

    override func awakeFromNib() {
        super.awakeFromNib()

        isAccessibilityElement = false
        shouldGroupAccessibilityChildren = true
        accessibilityElements = [deliveringAddressLabel, addressLabel, addressSwitch]
    }
}

Link:

Accessibility events

Description:

iOS sends several accessibility events to the applications. They are sent when accessibility options are changed. For example, if VoiceOver is deactivated, the running applications will receive the UIAccessibilityVoiceOverStatusChanged event. This is very useful when used simultaneously with UIAccessibilityIsVoiceOverRunning.

Let's say the application behaves differently when VoiceOver is turned on. This is detected by the UIAccessibilityIsVoiceOverRunning method. What happens if VoiceOver is disabled? This is when the system events can be used. By listening to these events, it is possible to dynamically change how the application behaves.

Example:

We call the “voiceOverStatusDidChange” method when the VoiceOver state changes:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "voiceOverStatusDidChange:", name: UIAccessibilityVoiceOverStatusChanged, object: nil)

Link:

Text size

Description:

Dynamic font size on iOS is very tricky. Since iOS7, it is possible to use an API to make the text size dynamic according to the phone settings. If we summarize this API, you must:

  • Use the system fonts for the application, [UIFont preferredFontForTextStyle:UIFontTextStyle…]. You can also use custom fonts only if they inherit from UIFont.
  • Listen to the font size settings change events UIContentSizeCategoryDidChangeNotification, [[NSNotificationCenter defaultCenter] addObserver:… selector:@selector(…) name: UIContentSizeCategoryDidChangeNotification object:…];
  • When handling the font size change event, you must redisplay the affected elements.

We must also be careful that the containers fit their contents: using constraints is the best way to perform this task.

Link:

Reading order

Description:

Redefining the VoiceOver reading order is done using the UIAccessibilityContainer protocol. The idea is to have a table of elements that defines the reading order of the elements. It is often very useful to use the shouldGroupAccessibilityElement attribute so we have a precise order but for a part if the view only (the rest of the view will be read using the “logical” order)

Example:

Table cell containing form elements:

class AddressTableViewCell: UITableViewCell {

    @IBOutlet weak var deliveringAddressLabel:  UILabel!
    @IBOutlet weak var addressLabel:            UILabel!
    @IBOutlet weak var switchLabel:             UILabel!
    @IBOutlet weak var addressSwitch:           UISwitch!

    override func awakeFromNib() {
        super.awakeFromNib()

        isAccessibilityElement = false
        shouldGroupAccessibilityChildren = true
        accessibilityElements = [deliveringAddressLabel, addressLabel, addressSwitch]
    }
}

Link: