VoiceOver and Scrolling in OpenGL backed UIViews

715 Views Asked by At

I am looking for advice on how to make a scrollable list drawn with OpenGL into a CAEAGLLayer backed UIViews accessible with VoiceOver. Ideally, the scrollable list in OpenGL should behave just like a native UIScrollView:

  1. When the user swipes left/right with three fingers I want to scroll up/down a page. This behavior is easily achieved by implementing the accessibilityScroll: method.
  2. When the user swipes left/right with one finger to go through all items in the list and the accessibility focus moves to the last/first partially visible item of the list I want the list to scroll slightly to ensure that the newly focused item is fully visible.
  3. When the user taps directly on the last/first partially visible item of the list to move the accessibility focus directly to it I want no scroll action to happen.
  4. When the user continues to swipe left/right with one finger while the last/first partially visible item of the list is selected, I want the list to scroll up/down one page to reveal previously hidden items.

As I said above, I know how to implement item #1, but after extensively studying Apple's documentation I still have no idea how to make items #2 to #4 work with the iOS accessibility API. I do think that those items are important for a great accessibility experience, though.

Any advice is greatly appreciated. Thanks!

1

There are 1 best solutions below

5
On BEST ANSWER

UIAccessibility is an informal protocol that vends the data necessary for accessibility clients such as VoiceOver to interpret and navigate a user interface. UIKit classes implement the full suite of methods in this protocol, rendering most views accessible by default. However, interface elements that aren't directly backed by a UIView, such as the view containing your CAEAGLLayer, must manually implement all relevant accessibility methods.

The best, and often only, way to render this content accessible is to implement a shadow hierarchy of UIAccessibilityElement objects corresponding to the content of the scene. To do so, you must declare the UIView in the UIKit view hierarchy to be a UIAccessibilityContainer. This is a two step process. First, you must ensure that the view backing your container is not, itself, an accessibility element, either by setting or overriding isAccessibilityElement to return false. Second, you must implement the four methods defined in the informal UIAccessibilityContainer protocol for accessing child accessibility elements:

  • func accessibilityElementCount()
  • func index(ofAccessibilityElement element: Any)
  • func index(ofAccessibilityElement: Any)
  • var accessibilityElements: [Any]?

In most cases, you will want to maintain an array of UIAccessibilityElement objects, each representing one component of your custom-rendered user interface. This array can be used to fulfill your backing view’s contract as an UIAccessibilityContainer. You can then extend elements to behave like scroll views, buttons, and other controls using methods in the UIAccessibility protocol.

To accomplish #1, you will want to implement accessibiilityScroll() on the an element in your accessibility hierarchy to respond to scrolling actions, returning true and posting a UIAccessibilityPageScrolledNotification with a string parameter describing the new scroll position. It sounds like you have this part working without issue.

Requirements #2, #3, and #4 all require programmatically scrolling your view when an element receives focus (or, in the case of #3, opting to do nothing). This can be accomplished using the UIAccessibilityFocus informal protocol. In particular, each element can respond to the func accessibilityElementDidBecomeFocused() and communicate its focus status back up to the object responsible for scrolling the view. If this triggers a paginated scroll, don't forget to post the page scrolled notification discussed, above.