UICollectionView 跳动滚动

UICollectionView jumpy scrolling

我正在尝试创建一个可以容纳 "views" 不同大小等的界面。为此,我使用 UICollectionView 和这些单元格:

-(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;
}

一切似乎都正常,直到卡片的数量变得太大而我不得不滚动。滚动后抬起手指时,视图会跳动一点。我还遇到了其他一些奇怪的 UI 问题。根据我的阅读,这与滚动期间单元格的重新加载有关。什么是防止这种情况的正确方法?

请务必注意,内容是动态的。用户应该能够在运行时添加和删除卡片。

谢谢

您可以将集合视图单元子类化,并在创建时执行一次所有层初始化,而不是每次重新使用时都执行一次。

此外,当重复使用这样的单元格时,您应该检查现有单元格是否已经添加了 Card。重用时,afaik iOS 不会自动删除添加的子视图(因此对于重用的单元格,addSubview 可以使用不同的卡片执行多次,您可以通过视图调试进行检查)。

您也可以延迟加载单元格内容。

我从您的代码中了解到,在每次调用 cellForItemAtIndexPath 时,您都会创建一个新的卡片对象并将其作为子视图添加到单元格中。

你实际上并没有使用重复使用细胞的力量。 尝试在单元格上添加 UI 元素(无论您使用的是故事板还是 xib)。然后根据索引路径设置那些元素的值。这将消除滚动的延迟。

同时导入 <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];

您在评论中表示您正在使用 CHTCollectionViewWaterFallLayout。 我自己在一个应用程序中使用它,从性能的角度来看,它不是最优的(我现在查看了源代码,关键点还是一样的)。我必须做一些调整才能让它顺利进行。

首先,如果您的目标是 iOS 8 及更高版本UICollectionView 已经有了巨大的改进,您绝对应该确保这一点尽可能使用。 2014 WWDC 视频 What's New in Table and Collection Views 为您提供了一个很好的概述。重点是iOS8在UICollectionViewLayoutInvalidationContext的基础上增加了三个新方法:

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

将这些方法与 UICollectionViewLayout invalidationContextForBoundsChange: 结合使用,您可以极大地减少布局需要处理的项目数量。这将为您提供最佳性能。

如果像我一样,您还需要瞄准 iOS 7,事情会变得更加复杂。您可以改进布局中的两个关键方法:

  • 首先,shouldInvalidateLayoutForBoundsChange: 调查更改的边界并确保仅在绝对必要时才使整个布局无效。
  • 其次,prepareLayout,仅在整个布局失效时才调用。

在我的例子中,我必须添加浮动 headers,所以我在 shouldInvalidateLayoutForBoundsChange: 中设置了一个 属性 shouldInvalidateAll,我在 prepareLayout 中检查了它知道我是否真的需要从头开始准备布局。

其实不是那么容易(但可能)顺利的,但我希望这为您的版本改进设置了一个起点。

我有点明白了。我的一些卡片包含图像,每次我滚动时,系统都会从内存中重新加载所有这些图像。这使得滚动相对缓慢且不稳定。