UICollectionView jumpy scrolling

2019-08-15 20:11发布

I am trying to create an interface that can hold "views" of different sizes etc. For this I am using a UICollectionView with these cells:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    Card *card = [[[usermanager getSelectedUser] getCards] objectAtIndex:indexPath.item];
    UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cardCell" forIndexPath:indexPath];
    [cell addSubview:card];

    [cell.layer setBorderColor:[UIColor redColor].CGColor];
    [cell.layer setBorderWidth:1.0f];
    [cell.layer setMasksToBounds:NO];
    return cell;
}

Everthing seems to work fine until the amount of cards becomes too big and I have to scroll. The view jumps a little bit when I lift my finger after scrolling. And I get some other weird UI issues. From what I have read this has to do with the reloading of the cells during scrolling. What would be the proper way to prevent that?

It is very important to note that the content is dynamic. The user should be able to add and remove cards at runtime.

Thanks

4条回答
Fickle 薄情
2楼-- · 2019-08-15 20:15

You could subclass the collection view cell and perform all layer initialization once when it's created and not everytime it is reused.

Also, when reusing a cell like this, you should check if the existing cell has already added a Card. When reusing, afaik iOS doesn't automatically remove added subviews (so for a reused cell addSubview could execute multiple times with different Cards, you can check this with view debugging ).

You also could lazy-load the cell content.

查看更多
We Are One
3楼-- · 2019-08-15 20:19

You stated in a comment that you are using CHTCollectionViewWaterFallLayout. I used it myself in an application and from a performance perspective, it is not optimal (I checked the source code right now, and the crucial points still are the same). I had to do several tweaks to get it smooth.

First off, if you are targeting iOS 8 and above, there have been made huge improvements to UICollectionView, which you should absolutely make sure to use if possible. The 2014 WWDC video What's New in Table and Collection Views gives you a good overview. The key point is that iOS 8 adds three new methods to UICollectionViewLayoutInvalidationContext:

- invalidateItemsAtIndexPaths:
- invalidateSupplementaryElementsOfKind:atIndexPaths:
- invalidateDecorationElementsOfKind:atIndexPaths:

Using these methods together with UICollectionViewLayout invalidationContextForBoundsChange: you can narrow down extremely the amount of items that your layout needs to handle. This will give you an optimal performance.

If, like me, you also need to target iOS 7, things get more complicated. There are two crucial methods in your layout that you can improve:

  • First, shouldInvalidateLayoutForBoundsChange: where you investigate the changed bounds and make sure that you invalidate the complete layout only if absolutely necessary.
  • Second, prepareLayout, which should be called only if the complete layout should be invalidated.

In my case, I had to add floating headers, so I set a property shouldInvalidateAll in shouldInvalidateLayoutForBoundsChange:, which I checked in prepareLayout to know if I really needed to prepare the layout from scratch.

It's actually not that easy (but possible) to get it smooth, but I hope that this sets a starting point for your version to improve.

查看更多
beautiful°
4楼-- · 2019-08-15 20:34

As I can understand from your code, on every cellForItemAtIndexPath call you are making a new card object and adding it as a subview on the cell.

You are not actually using the power of reusing cell. Try to add UI elements on the cell (storyboard or xib whatever you are using). Then set the values of those elements based on the index path. That would remove the lag in scrolling.

Also Import <QuartzCore/QuartzCore.h>

[cell.layer setShadowPath:[[UIBezierPath
                            bezierPathWithRect:self.bounds] CGPath]];

[cell.layer setBorderColor:[UIColor redColor].CGColor];    
[cell.layer setBorderWidth:1.0f];
[cell.layer setMasksToBounds:NO];
查看更多
Rolldiameter
5楼-- · 2019-08-15 20:34

I sort of figured it out. Some of my cards contained images, and everytime I would scroll the system would reload all these images from the memory. This made scrolling relatively slow and choppy.

查看更多
登录 后发表回答