Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating contentOffset changes in a UICollectionView on deleteItemsAtIndexPaths

I have a UICollectionView to which cells can be added and removed. I'm making these changes using performBatchUpdates, and the layout is animating as expected.

The problem comes when I scroll to the end of the content and delete an item such that the contentSize decreases: This causes the contentOffset to change, but the change isn't animated. Instead, the contentOffset jumps immediately after the deletion animation completes. I've tried manually updating the contentOffset alongside the deletion but that didn't work for me either.

I'm using a custom layout, but I see the same behaviour with a standard flow layout, using the following code:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.items.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    UILabel *label = (UILabel *)[cell viewWithTag:1];
    label.text = [self.items objectAtIndex:indexPath.item];
    return cell;
}

- (IBAction)addItem:(UIBarButtonItem *)sender
{
    self.runningCount++;
    [self.items addObject:[NSString stringWithFormat:@"Item %u",self.runningCount]];
    [self.collectionView performBatchUpdates:^{
        [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.items.count-1 inSection:0]]];
    } completion:nil];
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self.items removeObjectAtIndex:indexPath.item];

    [self.collectionView performBatchUpdates:^{
        [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
    } completion:nil];
}

I feel like I must be missing something obvious, but this has me stumped.

like image 756
knellr Avatar asked Jan 12 '23 13:01

knellr


1 Answers

The animation glitch is seen where the contentSize of the collection view shrinks such that its height or width becomes smaller than (or equal to) that of the collection view bounds.

It's possible to force the expected animation in the batch update block using setContentOffset:animated: and similar, but this relies on knowing the projected content size after deletion. Content size is managed by the collection view layout, but since we haven't actually deleted the cell yet, we can't just ask the layout (or we'd get the old size).

To work around this I implemented the targetContentOffsetForProposedContentOffset: method in my custom layout to adjust the content offset as desired. The code below, which only accounts for Y offset, was sufficient for my needs:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    if (self.collectionViewContentSize.height <= self.collectionView.bounds.size.height)
    {
        return CGPointMake(proposedContentOffset.x,0);
    }
    return proposedContentOffset;
}

I tried this out in a direct UICollectionViewFlowLayout subclass and it does the job there too.

like image 76
knellr Avatar answered Jan 24 '23 02:01

knellr