Light's Blog

The best or nothing.

UICollectionView iOS 10 New Features

| Comments

Background

iPhone屏幕的刷新频率固定为60fps,为了达到流畅的滑动效果,iOS应用展示必须满足该条件。当帧率很低时,就会出现明显的卡顿现象。

60fps相当于每帧16.67毫秒,在这么短的时间内collection view可能并不能完成从相对较慢的数据源加载数据。为了提升collection view性能,一个常用的技巧是使cellForItemAtIndexPath尽可能快的返回cell,比如异步加载网络图片等。为了进一步提高collection view性能,并且尽量减少开发者的工作,在iOS 10中引入了新特性。

UICollectionView API变化

新增UICollectionViewDataSourcePrefetching协议

1
2
3
4
5
6
7
8
9
10
11
@protocol UICollectionViewDataSourcePrefetching <NSObject>

@required
// indexPaths are ordered ascending by geometric distance from the collection view
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);

@optional
// indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -collectionView:prefetchItemsAtIndexPaths:
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths  NS_AVAILABLE_IOS(10_0);

@end

这两个方法均异步执行,可用于异步加载图片等。

新增prefetchDataSource代理

1
@property (nonatomic, weak, nullable) id<UICollectionViewDataSourcePrefetching> prefetchDataSource NS_AVAILABLE_IOS(10_0);

新增prefetchingEnabled属性

1
@property (nonatomic, getter=isPrefetchingEnabled) BOOL prefetchingEnabled NS_AVAILABLE_IOS(10_0);

Prefetching

当collection view滑动速率将要超过cellForItemAtIndexPath返回cell的速率时,collection view会调用prefetchItemAtIndexPaths:方法。

collection view会把可能即将需要展示的cell的IndexPath放入数组中传递给prefetch方法。这为我们提供了预处理数据机会。比如,当我们需要加载网络图片时,可以在prefetch方法中请求网络数据,并把下载的数据插入到data source中,为cellForItemAtIndexPath的使用做准备。

当collection view滑动方向改变时,collection view会调用cancelPrefetchingForItemsAtIndexPaths方法。

该方法的目的是取消原本可能即将展示的cell的预加载数据工作。参数同样是IndexPath的数组。

UICollectionView Cell生命周期变化

UICollectionViewCell Lifecycle: iOS <= 9

UICollectionViewLifecycle_iOS_9

  1. 首先,调用cellForItemAtIndexPath:,从复用队列中弹出一个cell,准备对其调用prepareForReuse
  2. 然后,根据需求设置cell的内容,比如labels等。
  3. cell即将出现时,调用collectionView:willDisplayCell:forItemAtindexPath:
  4. cell消失时,调用collectionView:didEndDisplayingCell:forItemAtIndexPath:。此时cell会重新进入复用队列,等待复用。
  5. 当用户向相反方向再次把cell滑回屏幕时,会重新从第一步开始执行。

UICollectionViewCell Lifecycle: iOS 10

UICollectionViewLifecycle_iOS_10

在iOS 10中,前3个步骤与iOS 9是相同的,新的变化发生在cell滑出屏幕的时候。

当调用collectionView:didEndDisplayingCell:forItemAtIndexPath:后,cell不会立刻进入复用队列,系统会keeps it around for a bit。相当于会缓存该cell一小段时间,在这段时间内如果该cell再次回到屏幕中,便不会重新调用cellForItemAtIndexPath:,而是直接显示。

至于系统会缓存多久,官方并没有给出明确的时间,感觉跟程序运行时开销有关。

如果想关闭该功能,需要设置collectionView.prefetchingEnabled = NO;

multiple_cells

collection view包含多列的情况,主要体现cell的独立性

当某一行需要展示时,每个cell独立出队并调用cellForItemAtIndexPath:方法;

当该行即将展示时,每个cell调用willDisplayCell:atIndexPath:

总结

  • 这些变化对开发者都是透明的,对开发者来说只需利用好prefetch特性。
  • prefetch进一步提升了collection view的性能,尤其是获取cell数据开销比较大或者比较慢时。
  • 每个cell独立出队,单独设置,确保cell在展示之前总是ready。
  • UITableView拥有相同的新特性。

参考资料:

WWDC2016 UICollectionView相关视频

Adoption Curve Dot Net

little bites of cocoa

Comments