TeehanLax / UICollectionView-Spring-Demo

A demonstration of UICollectionView and UIKit Dynamics
MIT License
473 stars 71 forks source link

Deleting cells with variable heights #10

Open wcrtr opened 10 years ago

wcrtr commented 10 years ago

Hey,

Great demo / project!

I've been experimenting with using the TLSpringFlowLayout with a UICollectionView with dynamically sized items, and everything works like a charm except deleting.

Basically what happens is I use a method to calculate the height of a cell based on it's content, which I do normally in the datasource here:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    CGRect estimatedRect = [height calculated from cell content]
    return CGSizeMake(300, estimatedRect.size.height);

}

All is fine and good until I try and delete one of these items with variable height. What happens is the dynamic animator returns the incorrect attributes here:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    return [self.dynamicAnimator itemsInRect:rect];
}

The layout attributes returned are the attributes of the deleted cell that was "above" the cell, not what should be retained by the cell itself. If I comment out the above code block, the attributes are retained correctly.

To say it another way, sizeForItemAtIndexPath is returning the correct size, but layoutAttributesForElementsInRect from the dynamic animator is returning the size from a deleted item.

Any ideas here? It's possible this is not a problem with this class itself, I'm just struggling to find answers and have isolated it here. I've also done a bunch of research into the issue and can't find much in way of answers so I thought it might be productive to start a thread here.

Thanks again for any insight!

ashfurrow commented 10 years ago

Tricky tricky. Deleting cells, in general, can be an exercise in frustration. I had to do some hacky things to get it to work for me, and that was without variable cell size! I honestly don't know where you'd start on this one – you've already covered the basics. Hmmm. I'll give it some more thought and see what I can think of. If all else fails, you can just reload the collection view when deletions occur.

Best of luck! Ash

wcrtr commented 10 years ago

I kinda got it working in a hacky way by appending the following to the prepareForCollectionViewUpdates method

        } else if(updateItem.updateAction == UICollectionUpdateActionDelete) {
            if([self.dynamicAnimator layoutAttributesForCellAtIndexPath:updateItem.indexPathAfterUpdate])
            {
                return;
            }

            [self.dynamicAnimator removeAllBehaviors];
            self.visibleIndexPathsSet = [NSMutableSet set];

            UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:updateItem.indexPathAfterUpdate];
        }

There are some limitations to this approach, one being that it seems like cells that weren't visible at the time of deletion get their heights set incorrectly still... but certainly closer.

It seems like if there were a way to delete the dynamic behaviors given known deleted cells, it might work without too much bother, but there doesn't seem to be a great way to do that save maybe keeping a set of all the dynamic behaviors and trying to figure out, at time of deletion, which ones to delete.

I'll keep tracking progress here if I make any...

atomkirk commented 10 years ago

See if my additions help: https://github.com/TeehanLax/UICollectionView-Spring-Demo/pull/12