Search code examples
iosuicollectionviewuiaccessibility

Incorrect accessibility order in UICollectionViewCell with viewForSupplementaryElement


I have a UITableViewCell which contains a UICollectionViewCell in each row. This collection view has supplementary views (a header) which appears at the top of the cell.

When navigating with VoiceOver enabled the cells are read in the correct order (header, collectionviewcell1, collectionviewcell2, ...) however, after the view has scrolled (caused by swiping left to right through the cells) the order becomes broken, reading out the UICollectionView cells and then the header.

What could cause this?

I have tried using the UIAccessibilityContainer protocol on the containing UITableViewCell and returning both the number of items in the UICollectionView, plus header, and for the indices returning the header at index 0 and the UICollectionViewCell at the given index. This always highlights the header first, but doesn't navigate through the UICollectionView cells.

I have also tried returning the count as 2 elements (header and UICollectionView) and returning those objects for accessibilityElementAtIndex. This does start with the header, but only reads out the first item in the CollectionView


Solution

  • It turns out the incorrect ordering was caused by using a UITableViewCell as the header view for the UICollectionView. A crash manifested when scrolling with VoiceOver turned on and I managed to stop this crash by returning the contentView of the cell in the UIAccessibilityContainer protocol.

    #pragma mark - UIAccessibilityContainer
    
    -(NSInteger)accessibilityElementCount {
    
        int headerCount = self.headerView ? 1 : 0;
        int footerCount = self.footerView ? 1 : 0;
    
        return ([self.dataValues count] + headerCount + footerCount;
    }
    
    -(id)accessibilityElementAtIndex:(NSInteger)index {
        if (index == 0) {
            if (self.headerView) {
                if ([self.headerView isKindOfClass:[UITableViewCell class]]) {
                    UIView *header = ((UITableViewCell *)self.headerView).contentView;
                    header.shouldGroupAccessibilityChildren = YES;
                    return header;
                }
                return self.headerView;
            }
        }
        if (self.headerView) index -= 1;
    
        if (index >= [self.dataValues count]) {
            if ([self.footerView isKindOfClass:[UITableViewCell class]]) {
                return ((UITableViewCell *)self.footerView).contentView;
            }
            return self.footerView;
        }
    
        return [self collectionView:_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
    }
    

    The answer to this question pointed me in the right direction:

    iOS VoiceOver crash (message sent to deallocated instance)