Recently one of my coworkers was trying to wrangle the accessibility label for a collection view they were working on. By default, performing the accessibility scroll gesture on a UIScrollView (or subclass) will move the scroll view’s content up or down by one “page”[1], and VoiceOver will read out your current position, e.x. “Page 2 of 15”[2]. The problem in this case was that their collection view lazily loaded its contents as the user scrolled, so the accessibility cursor would land on the view and dutifully read out “Page 1 of 1” when in reality there were many pages of content that simply hadn’t been loaded yet.

No one in company chat had a simple answer. It’s generally a bad idea to attempt to override or otherwise manipulate accessibility values that aren’t explicitly specified as overridable or set-able[3]—it seemed like implementing a custom UIAccessibilityContainer was the only way forward. UIAccessibilityContainer is sort of the nuclear option when implementing accessibility: you turn off all of the default support UIKit provides and implement your own from firstprinciples. This isn’t the end of the world: making a custom UIAccessibilityContainer can be surprisingly straightforward compared to implementing an entire custom control from scratch—but invites many of the same risks, such as missing edge case functionality or simply being far less robust than the framework version (not to mention the time it takes in implementation and debugging).

Luckily, there’s a better way—but it’s not referenced in the obvious UIAccessibility overview docs. As of iOS 6, Apple provides a simple answer for exactly this problem: enter UIScrollViewAccessibilityDelegate. It only has one method, –accessibilityScrollStatusForScrollView:, which returns the string VoiceOver will read when scrolling your view. So keep that in mind when implementing your own clever scroll views, whether a lazily-loaded collection or doing a three-card-monte to efficiently implement paging, and you’ll make your VoiceOver users' day just that much better. Don’t get carried away, though: if VoiceOver already accurately represents the state of your view, returning heavily customized text may be confusing for users expecting standard behavior.

Note that this protocol is declared in UIAccessibilityAdditions.h, which also declares UIPickerViewAccessibilityDelegate, a protocol that allows you to specify custom accessibility labels and hints for items in a UIPickerView. Personally, I’m going to keep an eye on that header to see if it grows in the future.

[1] Note that “page” here is not necessarily the same as a page in a scroll view with paging enabled. Without paging on a “page” is roughly one screen’s worth of content.

[2] UITableView behaves slightly differently: it reads out the visible rows and number of total rows, e.x. “Rows 5 through 10 of 50”