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!
UIAccessibilityis 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
UIAccessibilityElementobjects corresponding to the content of the scene. To do so, you must declare theUIViewin 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 overridingisAccessibilityElementto returnfalse. Second, you must implement the four methods defined in the informalUIAccessibilityContainerprotocol 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
UIAccessibilityElementobjects, 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 theUIAccessibilityprotocol.To accomplish #1, you will want to implement
accessibiilityScroll()on the an element in your accessibility hierarchy to respond to scrolling actions, returningtrueand posting aUIAccessibilityPageScrolledNotificationwith 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
UIAccessibilityFocusinformal 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.