Search code examples
iosiphoneobjective-cuicollectionviewz-index

UICollectionView top cell from zIndex not selected with overlapping cells


I have a UICollectionView with a number of cells that overlap each other.

I set their zIndex in a UICollectionViewLayout subclass with UICollectionViewLayoutAttributes so they appear in the desired order visually. The hit areas for touch evens work as expected with the cells with a higher zIndex coming though the collectionView:didSelectItemAtIndexPath: method when tapping the overlapping areas of the cell.

I transition to a UICollectionViewFlowLayout subclass and when I transition back to the first UICollectionViewLayout subclass the cells with a higher zIndex are not always selected when tapping the overlapping areas.

Further investigation revealed that the order of the UICollectionViewCells in the UICollectionView subviews is determining which cell is tapped.

I have written a method to reorder the subviews but this seems like a bit of a hack.

Is there a better way for the UICollectionView cells to return the expected index path in collectionView:didSelectItemAtIndexPath: based on how the cells appear visually?


Solution

  • From my testing, it seems that layout-to-layout transitions ignore the zIndex property of the pushed view controller's layout. This smells like a bug. You can work around the issue by reloading after the view controller appears:

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        // Reloading will clear selection, so we remember which index paths are selected before reload
        NSArray *selectedIndexPaths = self.collectionView.indexPathsForSelectedItems;
        NSIndexSet *allSections = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)];
    
        [self.collectionView performBatchUpdates:^{
    
            // First, reload to correct the zOrder
            [self.collectionView reloadSections:allSections];
    
            // Then, re-select the index paths
            if (selectedIndexPaths.count)
            {
                for (NSIndexPath *indexPath in selectedIndexPaths)
                {
                    [self.collectionView selectItemAtIndexPath:indexPath
                                                      animated:YES
                                                scrollPosition:UICollectionViewScrollPositionNone];
                }
            }
        } completion:nil];
    }
    

    It's still a bit of a hack, but less messy than manually reordering the collection view's subviews.