Search

Making Accessibility More Accessible, Part 2

Gabe Hoffman

5 min read

Apr 23, 2017

Making Accessibility More Accessible, Part 2

We’ve been talking about the high value that Apple has placed on Accessibility (AX) in iOS. We wanted to write a few posts to offer some tips and suggestions for covering a good majority of AX issues that creep up and suggest that this is something that should be done as you are going along. In the last post, we talked about a few questions you should always be keeping in the back of your mind that will flag common areas that can break accessibility.

One of those tips was to watch for any time you change the default behavior of an object. Say, when you want to turn an image into effectively a button. It needs to change state, and maybe even play a sound. That’s going to involve playing into UIAccessibilityTraits from your code. Which gets us into bitmasks. It’s certainly one of the rougher spots that still exists in Apple’s AX implementation. We’ll first talk you through a straight forward way of handling it, and then another more creative solution to make it more Swifty.

Wearing the UIAccessibilityTraits bitmask

So we have our image, that acts like a toggling button. Visually there is a clear design affordance when the image button changes colors. Somehow, this information needs to be conveyed through accessibility options to VoiceOver for people that are not looking at or can not see the screen.

No problem, you can set those UIAccessibilityTraits with code right? Sure can. But isn’t it a bitmask or some such C relic? Right again. Under the hood of UIAccessibilityTraits is a UInt64 bitmask. Each on or off bit represents a different trait. So if you’ve been looking for a practical application of bitmasks since CS 101, you’re in luck. For the rest of us, this code can look a little crusty. Even seasoned pros can get confused on how to set and clear bits in Swift. It all starts off pretty okay. Just a simple | pipe to OR the options together.

@IBOutlet var imageButton: UIImageView!
// setting in my code somewhere
imageButton.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitImage | UIAccessibilityTraitPlaysSound 

That’s all fine and good, if a bit longish. It is certaintly wrapping multiple lines. Now let’s handle clicking the image. That means we need to use the bitwise OR operator again to toggle the right bits on without clearing any of the other bits that are already set.

imageButton.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitImage | UIAccessibilityTraitPlaysSound | UIAccessibilityTraitSelected

// or more simply
imageButton.accessibilityTraits |= UIAccessibilityTraitSelected

Now to turn it back off. This is where it can really get cryptic looking. The most simple way is to use the bitwise AND along with the bitwise NOT to unset that tricky little bit. (Remember all that fun logic from your CS classes?)

imageButton.accessibilityTraits &= ~UIAccessibilityTraitSelected

What if you want to see if a trait is already set?

let isSelected = imageButton.accessibilityTraits & UIAccessibilityTraitSelected

Problem solved? Yeah, mostly. It’s fine. Except when you come back to it in a few months. Or the next guy has to figure it what’s going on. Or when you have to explain it. Or when you have to push it up on a PR. Or even worse, when you have to check someone else’s bit logic on their PR.

It really is not the most readable thing write. Writing code is easy; reading it is hard. How can we make this more readable? Do you see this type of code anywhere else in your Swift code? Of course not! In Swift, we have this wonderful thing called an OptionSet. An OptionSet is a type that presents a mathematical set interface to a bitmask. Which is to say, it could make UIAccessibilityTraits actually pretty easy. Take a look.

It’s Easier with OptionSets

struct AccessibilityTraits: OptionSet {
    let rawValue: UIAccessibilityTraits

    static let button = AccessibilityTraits(rawValue: UIAccessibilityTraitButton)
    static let link = AccessibilityTraits(rawValue: UIAccessibilityTraitLink)
    static let image = AccessibilityTraits(rawValue: UIAccessibilityTraitImage)
    static let selected = AccessibilityTraits(rawValue: UIAccessibilityTraitSelected)
    static let playsSound = AccessibilityTraits(rawValue: UIAccessibilityTraitPlaysSound)
    static let keyboardKey = AccessibilityTraits(rawValue: UIAccessibilityTraitKeyboardKey)
    static let staticText = AccessibilityTraits(rawValue: UIAccessibilityTraitStaticText)
    static let summaryElement = AccessibilityTraits(rawValue: UIAccessibilityTraitSummaryElement)
    static let notEnabled = AccessibilityTraits(rawValue: UIAccessibilityTraitNotEnabled)
    static let updatesFrequently = AccessibilityTraits(rawValue: UIAccessibilityTraitUpdatesFrequently)
    static let searchField = AccessibilityTraits(rawValue: UIAccessibilityTraitSearchField)
    static let startsMediaSession = AccessibilityTraits(rawValue: UIAccessibilityTraitStartsMediaSession)
    static let adjustable = AccessibilityTraits(rawValue: UIAccessibilityTraitAdjustable)
    static let directInteraction = AccessibilityTraits(rawValue: UIAccessibilityTraitAllowsDirectInteraction)
    static let causesPageTurn = AccessibilityTraits(rawValue: UIAccessibilityTraitCausesPageTurn)
    static let header = AccessibilityTraits(rawValue: UIAccessibilityTraitHeader)

    static func == (lhs: UIAccessibilityTraits, rhs: AccessibilityTraits) -> Bool {
        return lhs == rhs.rawValue
    }

    static func == (lhs: AccessibilityTraits, rhs: UIAccessibilityTraits) -> Bool {
        return lhs.rawValue == rhs
    }
}

Add this struct into your code and you’ll get all the readability of the OptionSet for use in setting up your .accessibilityTraits. Here’s what using it could look like.

@IBOutlet var imageButton: UIImageView!
@IBOutlet var stateLabel: UILabel!

var imageButtonIsOn = false {
    didSet {
        updateUI() // change the UI
        imageButton.accessibilityTraits = imageButtonTraits.rawValue
    }
}

var imageButtonTraits: AccessibilityTraits {
    if imageButtonIsOn {
        return [.button, .image, .selected, .playsSound]
    } else {
        return [.button, .image, .playsSound]
    }
}

That’s incredibily readable code now! Now when the image is tapped (remember to turn User Interaction Enabled to ON and to make sure Accessibilty is Enabled, both from the Storyboard), you toggle the state of imageButtonIsOn and that will both update the UI and change the accessibilityTraits with the button’s didSet method. This is a great pattern to use in your code.

You also get the benefit of simple readable checks to see whether or not a trait or a collection of traits is present.

imageButtonTraits.contains(.selected)

imageButtonTraits.isEmpty

If you neeed to add or remove traits from code, it is as simple as normal set functions and assigning the new rawValue.

anotherButtonTraits.insert(.selected)
anotherButtonTraits.remove(.selected)
imageButton.accessibilityTraits = anotherButtonTraits.rawValue

Now UIAccessibilityTraits feels more Swifty and less like a logic puzzle from a coding interview.

Other Rough Spots

Hopefully that helps with one part of supporting AX in your app, but there are few other areas that can be tricky… we’ll get into those in the next post. What else gives you problems? What rough spots have you run across and how have you addressed them?

Steve Sparks

Reviewer Big Nerd Ranch

Steve Sparks has been a Nerd since 2011 and an electronics geek since age five. His primary focus is on iOS, watchOS, and tvOS development. He plays guitar and makes strange and terrifying contraptions. He lives in Atlanta with his family.

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News