自定义UICollectionViewLayout(一)UICollectionViewLayout Class Reference翻译

官方文档: UICollectionViewLayout Class Reference

继承关系:

  • NSObject
    • UICollectionViewLayout
      • UICollectionViewFlowLayout
      • UICollectionViewTransitionLayout

遵从协议:

  • NSCoding
  • NSObject

UICollectionViewLayout是一个抽象的基础的layout object,你可以使用UICollectionViewLayout的子类来生成布局信息,以供collection view使用。layout object决定如何放置cells、supplementary views、 decoration views,并在collection view请求这些信息时能够及时提供。当collection view获取到这些信息之后,会通知相应的视图来呈现。

当你需要使用布局对象时,必须要使用UICollectionViewLayout的子类。当你考虑自定义UICollectionViewLayout的子类时,请先查看一下 UICollectionViewFlowLayout是否已经可以满足你的要求。

Subclassing Notes(子类注意事项)

layout object的主要任务是向UICollectionView提供cells、supplementary views、 decoration views的位置、大小信息。layout object并不为它的使用者创建任何视图,创建视图的工作由collection view的 data source来完成。layout object只定义可视元素的位置、大小信息。

UICollectionView需要布局三类可视元素:

  1. Cells是layout object管控布局的主要对象。每个cell用于呈现数据集合中的单个元素。一个collection view可以拥有一组或多组cells,每组被称为一个section。layout object的主要任务就是在collection view的内容区域排列这些cells。
  2. Supplementary views(supplementary:补充的 ; 额外的; 附加的)supplementary views 也用于呈现数据,但和cells有所区别:

    • supplementary views不能够被用户选中,而cells能够被用户选中;
    • supplementary views主要用于section或者整个collection view的header 或footer;
    • supplementary views是可选的,如何使用、在哪放置都在layout object中定义。
  3. Decoration views顾名思义是纯粹的装饰性视图,它不可选,与collection view的数据无关。和supplementary views一样,如何使用、在哪放置都在layout object中定义。

collection view会多次向自己的layout object请求这些可视元素的布局信息,每个cell或其它视图能够呈现在屏幕上对应位置所依赖的信息全都来自layout object。同样的,collection view新增或删除items时,layout object会为这些新增或删除的items生成额外的布局信息。不过,collection view只会对屏幕上可见的对象进行布局。

Methods to Override(需要重写的方法)

每个 layout object 都应该实现以下几个方法:

1
2
3
4
5
6
7
8
9
10
11
collectionViewContentSize

layoutAttributesForElementsInRect:

layoutAttributesForItemAtIndexPath:

layoutAttributesForSupplementaryViewOfKind:atIndexPath: (if your layout supports supplementary views)

layoutAttributesForDecorationViewOfKind:atIndexPath: (if your layout supports decoration views)

shouldInvalidateLayoutForBoundsChange:

这些方法提供了collection view将内容呈现在屏幕上所需的基本的布局信息,当然,你的布局中如果不考虑使用 support supplementary 或者 decoration views ,那就不要实现相应的方法。

当collection view的有数据项插入或者删除时,collection view会请求layout object更新布局信息。尤其是,当移动、新增或者删除数据时,必须更新这些数据项的布局信息以反映它们新的位置。对于移动的数据项,collection view使用标准的方法(?)取得其更新后的布局信;而对于新增或者删除的数据项,collection view会请求一些特殊的方法,你需要重写这些方法以提供合适的布局信息。

1
2
3
4
5
6
7
8
9
10
11
initialLayoutAttributesForAppearingItemAtIndexPath:

initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingItemAtIndexPath:

finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:

除了这些方法外,你还可以重写以下方法:

1
2
3
prepareForCollectionViewUpdates:

finalizeCollectionViewUpdates
  • 重写 prepareForCollectionViewUpdates: 处理任何布局相关的准备工作;
  • 重写 finalizeCollectionViewUpdates 用于向全局animation block中添加动画,或者实现最终的布局相关任务。

Optimizing Layout Performance Using Invalidation Contexts(使用“让上下文无效”的方法来优化布局呈现)

当设计你的自定义布局时,你可以只让真正改变的那一部分元素无效,从而提高效率。当你改变items时,可以调用 invalidateLayout 方法,强制collection view计算其所有布局信息。显然,这并不高效。更好的解决办法是重新计算真正发生改变的布局信息,即invalidation context。invalidation context让你具体说明layout的哪一部分发生了改变,这样一来 layout object 可以使重新计算的部分最小化。

当你需要自定义invalidation context时,需要继承 UICollectionViewLayoutInvalidationContext 。在你的这个子类中,定义相关属性,这些属性用以描绘你的布局数据中哪些可以独自重新计算。在运行时一旦需要使布局失效,创建你自定义invalidation context的实例,并指出实例中的属性是基于哪些布局信息发生改变而起作用的,并将这个实例传递给布局对象的 invalidateLayoutWithContext: 方法。你自定义的布局对象所实现的invalidateLayoutWithContext:方法中,可以使用该invalidation context实例,只重新计算部分布局信息而不是计算全部。
(这一段不太好翻译,大家觉得翻译有问题的可看下原文:To define a custom invalidation context for your layout, subclass the UICollectionViewLayoutInvalidationContext class. In your subclass, define custom properties that represent the parts of your layout data that can be recomputed independently. When you need to invalidate your layout at runtime, create an instance of your invalidation context subclass, configure the custom properties based on what layout information changed, and pass that object to your layout’s invalidateLayoutWithContext: method. Your custom implementation of that method can use the information in the invalidation context to recompute only the portions of your layout that changed.)

当你自定义invalidation context之后,需要重写 invalidationContextClass方法返回这个自定义invalidation context。当collection view需要invalidation context时,它会总是创建你指定invalidation context类实例。因此通过invalidationContextClass方法返回自定义invalidation context,可以确保你的布局对象总是持有这个自定义invalidation context。

Getting the Collection View Information(获取Collection View的信息)

1
2
3
collectionView  Property 

- collectionViewContentSize

Providing Layout Attributes(提供布局属性)

1
2
3
4
5
6
7
8
+ layoutAttributesClass
- prepareLayout
- layoutAttributesForElementsInRect:
- layoutAttributesForItemAtIndexPath:
- layoutAttributesForSupplementaryViewOfKind:atIndexPath:
- layoutAttributesForDecorationViewOfKind:atIndexPath:
- targetContentOffsetForProposedContentOffset:
- targetContentOffsetForProposedContentOffset:withScrollingVelocity:

Responding to Collection View Updates (相应Collection View更新)

1
2
3
4
5
6
7
8
9
10
11
12
- prepareForCollectionViewUpdates:
- finalizeCollectionViewUpdates
- indexPathsToInsertForSupplementaryViewOfKind:
- indexPathsToInsertForDecorationViewOfKind:
- initialLayoutAttributesForAppearingItemAtIndexPath:
- initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:
- initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
- indexPathsToDeleteForSupplementaryViewOfKind:
- indexPathsToDeleteForDecorationViewOfKind:
- finalLayoutAttributesForDisappearingItemAtIndexPath:
- finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:
- finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:

Invalidating the Layout(使布局失效)

1
2
3
4
5
6
7
- invalidateLayout
- invalidateLayoutWithContext:
+ invalidationContextClass

- shouldInvalidateLayoutForBoundsChange:
- invalidationContextForBoundsChange:
- shouldInvalidateLayoutForPreferredLayoutAttributes:withOriginalAttributes:
- invalidationContextForPreferredLayoutAttributes:withOriginalAttributes:

Coordinating Animated Changes (协调动画改变)

1
2
- prepareForAnimatedBoundsChange:
- finalizeAnimatedBoundsChange

Transitioning Between Layouts (Layouts之间过渡)

1
2
3
- prepareForTransitionFromLayout:
- prepareForTransitionToLayout:
- finalizeLayoutTransition

Registering Decoration Views(注册Decoration Views)

1
2
- registerClass:forDecorationViewOfKind:
- registerNib:forDecorationViewOfKind: