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:
- 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.
- 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.
- 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.
- 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!
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 aUIView
, such as the view containing yourCAEAGLLayer
, 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 theUIView
in the UIKit view hierarchy to be aUIAccessibilityContainer
. 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 overridingisAccessibilityElement
to returnfalse
. Second, you must implement the four methods defined in the informalUIAccessibilityContainer
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 anUIAccessibilityContainer
. You can then extend elements to behave like scroll views, buttons, and other controls using methods in theUIAccessibility
protocol.To accomplish #1, you will want to implement
accessibiilityScroll()
on the an element in your accessibility hierarchy to respond to scrolling actions, returningtrue
and posting aUIAccessibilityPageScrolledNotification
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 thefunc 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.