---
title: Grouping elements for better accessibility on iOS
teaser: 'Thinking about logical view groupings can help your apps'' VoiceOver users
  navigate more fluidly.

  '
tags: accessibility,ios
author: Sid Raval
published_on: 2019-03-12
---

On iOS, VoiceOver users can navigate a screen through a swipe right gesture.
Doing so causes iOS to change accessibility focus to the next accessible element
in your views' accessibility hierarchy. By default, many `UIKit` elements declare
themselves as [accessibility elements], e.g. `UILabel`, `UIButton`, &c.

The by-default accessibility of many iOS components is wonderful, but sometimes
we need to do a bit more work. As a concrete example, let's say that we have a
`UICollectionView` composed of cells like the following:

![collection view cell example](https://images.thoughtbot.com/blog-vellum-image-uploads/ZjwN0ktXSGeIryza1PQU_accessibility_example.png)

A straightforward implementation of the above view might look like this:

```swift
class ProductCard: UICollectionViewCell {
    @IBOutlet var image: UIImageView!
    @IBOutlet var brandName: UILabel!
    @IBOutlet var productName: UILabel!
    @IBOutlet var salePrice: UILabel!
    @IBOutlet var originalPrice: UILabel!
    @IBOutlet var discountLabel: UILabel!
    @IBOutlet var starView: UIImageView!
    @IBOutlet var reviewCount: UILabel!
    @IBOutlet var addToCartBtn: UIButton!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}
```

By default each UI element is its own accessibility element, which means this
card would take 9 swipes to navigate through! That can be exhausting,
particularly in a collection with many cells (especially if you have motor 
control concerns). Moreover, it makes more sense for some of these elements to be
grouped and read together; e.g. instead of VoiceOver reading the sale price ->
swipe right -> reading the original price, grouping the price labels into a
single accessibility element better explains the intention _and_ reduces the
number of gestures required to navigate the card.

We can make this a bit more sensible by grouping the brand name & product name;
the sale price, original price, & discount label; and the star view & review
count. I might do so as follows:

```swift
class ProductCard: UICollectionViewCell {
  /* ... */

  func setupAccessibility() {
    accessibilityElements = [image, brandName, salePrice, starView, addToCartBtn]

    let brandAndProductLabel = "\(productName.text) by \(brandName.text)"
    brandName.accessibilityLabel = brandAndProductLabel

    let priceLabel = "Sale price: \(salePrice.text); originally \(originalPrice.text); \(discountLabel.text)"
    salePrice.accessibilityLabel = priceLabel

    let starCount = /* code to find current rating */
    let numRatings = /* code to find the number of ratings */
    let ratingsLabel = "Rating: \(starCount) out of 5; based on \(numRatings) reviews"
    starView.accessibilityLabel = ratingsLabel
  }
}
```

In doing so we've done two things: one, reduce the number of navigation swipes
per cell from 9 to 5; two, logically group related labels to give our VoiceOver
users more context.

Before our changes, VoiceOver would read our card like this (a semicolon
indicates a swipe gesture):

> Image; thoughtbot; Product design and development;
> Five dollars; Ten dollars; You save fifty percent;
> Image; Nine thousand seven hundred eighty four;
> Add to cart button

After our changes:

> Image; Product design and development by thoughtbot;
> Five dollars, originally ten dollars, you save fifty percent;
> Rating: five out of five, based on nine thousand seven hundred eighty four reviews;
> Add to cart button

Apple has done a great job of building accessibility into the `UIKit` framework,
which allows many apps to be reasonably accessible by default. As we've seen,
with just a small amount of attention and code, we as developers can provide an
even better experience to app users with accessibility needs.

[accessibility elements]: https://developer.apple.com/documentation/uikit/accessibility/uiaccessibility
