UICollectionView 总结
这个视图是我遇到的 UIView 里面最复杂的吧。。。感觉学好了这个,UIScrollView 和 UITableView 都不算啥了。。。
UICollectionView有两个主要的特点:
其一:和tableView一样,即提供数据的UICollectionViewDataSource以及处理用户交互的UICollectionViewDelegate;
其二,对于cell的样式和组织方式,由于collectionView比tableView要复杂得多,因此没有按照类似于tableView的style的方式来定义,而是专门使用了一个类来对collectionView的布局和行为进行描述,这就是UICollectionViewLayout。这不仅是collectionView和tableView的最重要求的区别,也是整个UICollectionView的精髓所在。
1)基本视图
最简单的 UICollectionView 就是一个 GridView,可以以多列的方式将数据进行展示。标准的UICollectionView包含三个部分,它们都是UIView的子类:
- Cells:用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容
- Supplementary Views 追加视图 (类似Header或者Footer)
- Decoration Views 装饰视图 (用作背景展示,只在 UICollectionViewLayout 中提供)
2)视图的注册和重用
相比 UITableView 有两个主要变化:一是加入了对某个 Class 的注册,这样即使不用提供nib而是用代码生成的 view 也可以被接受为 cell 了;二是不仅只是 cell,supplementaryView也可以用注册的方法绑定初始化了。在对collection view的重用ID注册后,就可以像UITableView那样简单的写cell配置了:
在使用 UICollectionView 的时候,初始化一个 layout 给这个 UICollectionView,并且注册相应的 cell 和 supplementaryView,这样在之后的重用方法中,就没有必要像 UITableView 一样判断是否有 cell 或 supplementaryView——要是在重用队列里没有可用的cell的话,runtime将自动帮我们生成并初始化一个可用的cell.
3)cell
UICollectionView 的 cell 相对 UITableView 简单,由下而上分别为:
- cell :本身作为容器 view;
- backgroundView:cell 的背景;
- selectedBackgroundView:cell 被选中时的背景
- contentView:自定义内容会添加到这个 view 上。
4)UICollectionViewLayout
负责了将各个cell、Supplementary View和Decoration Views进行组织,为它们设定各自的属性——layoutAttributes。
Layout决定了UICollectionView是如何显示在界面上的。在展示之前,一般需要生成合适的UICollectionViewLayout子类对象,并将其赋予CollectionView的collectionViewLayout属性。
Apple为我们提供了一个最简单可能也是最常用的默认layout对象:UICollectionViewFlowLayout。Flow Layout简单说是一个直线对齐的layout,最常见的Grid View形式即为一种Flow Layout配置。
UICollectionViewFlowLayout 的重要属性有:
- itemSize:定义了每一个item的大小。通过设定itemSize可以全局地改变所有cell的尺寸,也可以定制尺寸
- spacing:指定 item 之间间距和每一行之间的间距,可以全局设置或者定制间距
- scrollDirection:确定 scroll view 的方向,将影响 Flow Layout 的基本方向和由 header 及 footer 确定的 section 之间的宽度
- header 和 footer:同样可以全局设置或者定制。根据滚动方向不同,高和宽中只有一个会起作用:垂直滚动时section间宽度为该尺寸的高,而水平滚动时为宽度起作用
- sectionInset:section 的缩进
5)UICollectionViewLayoutAttributes——这个类包含的属性有:
1 @property (nonatomic) CGRect frame 2 @property (nonatomic) CGPoint center 3 @property (nonatomic) CGSize size 4 @property (nonatomic) CATransform3D transform3D 5 @property (nonatomic) CGFloat alpha 6 @property (nonatomic) NSInteger zIndex 7 @property (nonatomic, getter=isHidden) BOOL hidden
UICollectionViewLayoutAttributes的实例中包含了诸如边框,中心点,大小,形状,透明度,层次关系和是否隐藏等信息。和DataSource的行为十分类似,当UICollectionView在获取布局时将针对每一个indexPath的部件(包括cell,追加视图和装饰视图),向其上的UICollectionViewLayout实例询问该部件的布局信息(在这个层面上说的话,实现一个UICollectionViewLayout的时候,其实很像是封装一个delegate,之后的例子中会很明显地看出),这个布局信息,就以UICollectionViewLayoutAttributes的实例的方式给出。
6)自定义UICollectionViewLayout
UICollectionViewLayout的功能为向UICollectionView提供布局信息,不仅包括cell的布局信息,也包括追加视图和装饰视图的布局信息。实现一个自定义layout的常规做法是继承UICollectionViewLayout类,然后重载下列方法:
//返回collectionView的内容的尺寸 -(CGSize)collectionViewContentSize //返回rect中的所有的元素的布局属性,一个包含UICollectionViewLayoutAttributes的NSArray -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect //UICollectionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息,通过不同的UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollectionViewLayoutAttributes: layoutAttributesForCellWithIndexPath: layoutAttributesForSupplementaryViewOfKind:withIndexPath: layoutAttributesForDecorationViewOfKind:withIndexPath: //返回对应于indexPath的位置的cell的布局属性 -(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath //返回对应于indexPath的位置的追加视图的布局属性,如果没有追加视图可不重载 -(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath //返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载 -(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath //当边界发生改变时,是否应该刷新布局。如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息。 -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
5)自定义 UICollectionViewLayout
在初始化一个UICollectionViewLayout实例后,会有一系列准备方法被自动调用,以保证layout实例的正确:
首先,-(void)prepareLayout将被调用,默认下该方法什么没做,但是在自己的子类实现中,一般在该方法中设定一些必要的layout的结构和初始需要的参数等。
之后,-(CGSize) collectionViewContentSize将被调用,以确定collection应该占据的尺寸。注意这里的尺寸不是指可视部分的尺寸,而应该是所有内容所占的尺寸。collectionView的本质是一个scrollView,因此需要这个尺寸来配置滚动行为。
接下来-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect被调用,这个没什么值得多说的。初始的layout的外观将由该方法返回的UICollectionViewLayoutAttributes来决定。
另外,在需要更新layout时,需要给当前layout发送 -invalidateLayout,该消息会立即返回,并且预约在下一个loop的时候刷新当前layout,这一点和UIView的setNeedsLayout方法十分类似。在-invalidateLayout后的下一个collectionView的刷新loop中,又会从prepareLayout开始,依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局。