Help customizing the accessibility of a large UICollectionView

Hello,

I am turning to this forum because I suspect I am "doing it wrong" when it comes to implementing VoiceOver accessibility in my collection view. I suspect this because the system has resisted everything I have tried to do, fought it tooth and nail, and I can't see any way to get this to work.

The Collection View

I have a collection view that displays a large dataset. It uses a custom collection view layout to create a spreadsheet-like view. It has hundreds of rows, and each row can have hundreds of items. The items in each row do not conform to specific column widths. Their width is defined by the data they display, and for the purposes of this discussion, can be considered to be arbitrary.

To the left of the "table" is a column of sticky headers whose position remains fixed in relation to the content. On top of the "table" is a row of headers, whose position also remains fixed.

The Problem

The default accessibility behavior that Apple has baked into UICollectionView is completely impractical for this application. Each row can contain hundreds of items, so a user who is attempting to navigate by swiping right would have to swipe through hundreds of items just to reach the second row (of hundreds).

The Desired Behavior

I want the user to be able to swipe through just the cells that are onscreen. To scroll, they can use the standard three-finger gesture. When scrolling occurs, VoiceOver should announce the range of data that is being displayed.

Attempted Solution 1: Setting the accessibilityElements array

I can set the accessibilityElements array of the UICollectionView to only contain the elements that are onscreen. I also can override the accessibilityScroll method to perform the paging upon a three-finger scroll. This works okay, but has some pretty fatal flaws:

  1. As the user swipes through elements, the collection view insists upon scrolling horizontally to try and fit the element into view. It also insists upon scrolling vertically to keep the focused element in the middle of the view. This not only causes the content offset to jump around wildly in a disorienting way, but it also brings content into view that VoiceOver does not know about because I have not added it to the accessibilityElements array. A low-vision user, or a user who pans their finger across the screen, would not be able to access those visible elements.

  2. VoiceOver refuses to read my paging announcement. No matter when I post a pageScrolled notification, the system will not read it.

Setting accessibilityFrame

In an attempt to fix the scroll jumping described above, I tried setting the accessibilityFrame of my collection view cells. This did nothing to alter the scroll jumping behavior, and had the added downside that, as the view jumped around, the accessibility frames did not follow it.

A bridge too far? Overriding contentOffset

I was about to override contentOffset on the collection view so that only I could set it. That would probably work. But it would do nothing to fix the paging announcement.

Attempted Solution 2: Ignore the Cells! Use proxy UIAccessibilityElements

I tried setting the accessibilityElements array of my collection view to a collection of UIAccessibilityElement instances whose accessibilityFrame matched the frame of the cells they represent. This worked pretty well! No more scrolling nonsense when swiping through cells, and my paging announcements were being read.

This approach has a different, equally fatal flaw: If the user attempts to three finger-scroll too quickly, the VoiceOver process will become confused. It acts as though the last selected element is the only element that exists; swiping right or left does nothing. Three finger-scrolling also does nothing.

As best as I can tell, it gets stuck with the last selected element as the only one it knows about. I have since replaced all of the elements in the collection view's accessibilityElements array and posted a layoutDidChange notification, which VoiceOver ignores completely. The only way out of this state is to tap on a cell, causing VoiceOver to refresh its collection of views that it knows about. I guess? No idea what's happening there.

Now what?

I'm at a complete and total loss. I'm at my wit's end. It feels like this seemingly simple customization is entirely impossible. Does anyone know what I'm doing wrong?

Thanks!

Answered by ElFrijol in 760070022

A couple days later, this is the solution I came up with:

  1. Use the collection view cells directly as accessibility elements, and not the UIAccessibilityElement subclass proxies.
  2. Make sure the collection view's accessibilityElements array contains only visible cells in the correct order.
  3. Make sure the accessiblityFrame of the cells gets clipped by the screen bounds.
  4. Override contentOffset on the collection view so that only my code can set it. Attempts by the system to set it while VoiceOver is running are ignored.

This is working very well. But, the worry persists that I'm just plain doing it wrong. Overriding contentOffset seems like something that should not be necessary, and I can't shake the feeling that it wouldn't be necessary if I were setting everything up correctly.

Which is to say that I'm still open to someone coming in and telling me how best to accomplish this. But for now, I'm moving on, and I hope this solution helps someone who is googling.

Turns out the pageScrolled announcement not getting read out was my fault. I had overlooked something as I made experimental code changes and I was actually passing nil instead of a string. The complaint about the scrolling while swiping still stands, though.

Accepted Answer

A couple days later, this is the solution I came up with:

  1. Use the collection view cells directly as accessibility elements, and not the UIAccessibilityElement subclass proxies.
  2. Make sure the collection view's accessibilityElements array contains only visible cells in the correct order.
  3. Make sure the accessiblityFrame of the cells gets clipped by the screen bounds.
  4. Override contentOffset on the collection view so that only my code can set it. Attempts by the system to set it while VoiceOver is running are ignored.

This is working very well. But, the worry persists that I'm just plain doing it wrong. Overriding contentOffset seems like something that should not be necessary, and I can't shake the feeling that it wouldn't be necessary if I were setting everything up correctly.

Which is to say that I'm still open to someone coming in and telling me how best to accomplish this. But for now, I'm moving on, and I hope this solution helps someone who is googling.

Help customizing the accessibility of a large UICollectionView
 
 
Q