Segmented Control Accessibility

Most of the posts here are aimed at the people who use the stuff we make. This one is aimed at fellow iOS developers, who might be searching for a solution to a particular problem.

That problem is that UISegmentedControl doesn’t offer good VoiceOver accessibility. This is unusual for Apple as their accessibility stuff is normally extremely strong.

(If you’re lost already, VoiceOver is the name of Apple’s technology for making devices usable by people who can’t see — amongst other things, by reading out the names of items as they pass under the touch of a user running their fingertip across the screen. UISegmentedControl is the technical name for those widgets that look like one long button, split up into pieces, where you can tap the pieces to select one of several options. If you open up the Maps app, it’s the thing that says “Search | Directions”. )

If your segmented control options contain text, those text labels will be read aloud by VoiceOver. This is a good start. However, unlike almost every other control in iOS, you can’t set customised VoiceOver descriptions. This is annoying for text-based controls — but fatal for image-based ones. There’s just no way to give them any useful description at all.

Not only that, but segmented controls don’t correctly set their “traits” (hints provided to VoiceOver to help it figure out how to describe a control) correctly, so it doesn’t tell you if an option is disabled.

Well, I went looking to see if anyone else had encountered this problem and solved it. I did find this helpful post on UISegmentedControl accessibility, but it didn’t work reliably for me.

The problem I found was that, while the code in that article did set the accessibility labels, they were being reset afterwards if the segmented control’s status changed — for example, if I enabled or disabled one of the options, or changed an image. I haven’t checked if this applies to all segmented controls, or just the ones that have been customised using the UIAppearance API, which I use extensively.

Worse still, the ordering of the UIViews was changed when this happened, so if you tried to apply the descriptions again after each change, the VoiceOver descriptions would be assigned to the wrong options!

Here’s the solution I came up with: A subclass of UISegmentedControl called AccessibleSegmentedControl. It simply adds one retained property, segmentAccessibilityLabels, which is unsurprisingly an NSArray of strings representing the VoiceOver labels to use for each segment.

Behind the scenes, it does the work of assigning these to the options, reassigning them when necessary, and it takes care of the fact that the order of the options gets scrambled. It also checks to see if the segmented control itself has a description (normally ignored by VoiceOver) and if so, applies it as a prefix.

Eventually, I’m sure Apple will update UISegmentedControl to fix these issues, rendering this code obsolete. But in the meantime, I’m making it public so that more apps can be accessible — not just “in theory”, but actually pleasantly — to VoiceOver users. The code is provided under the BSD license and archived in the kibble section of the website. Please note that it requires iOS 5 or later, though you can probably adapt it for earlier versions.

Update 2012-02-06: I’ve been informed that there is a way of setting descriptions for image-based segmented controls, although it’s not mentioned in the UISegmentedControl documentation. You can set the accessibilityLabel of the UIImage (yes, even though it’s not a view and again, the documentation doesn’t mention it supports the accessibility protocol) that you configure it with, and the segmented control will pick that up. Of course, there’s no way to do this when editing the UI in Xcode, so you’ll still have to set them up in code — and it still doesn’t acknowledge when items aren’t enabled. Hopefully these issues will get addressed in future iOS updates!