UICollectionView - 使用二

UICollectionViewLayout

终于到UICollectionView的精髓了…这也是UICollectionView和UITableView最大的不同。
UICollectionViewLayout可以说是UICollectionView的大脑和中枢,它负责了将各个cell、Supplementary View和Decoration Views进行组织,为它们设定各自的属性,包括但不限于:

    位置
    尺寸
    透明度
    层级关系
    形状
    等等等等…

Layout决定了UICollectionView是如何显示在界面上的。
在展示之前,一般需要生成合适的UICollectionViewLayout子类对象,并将其赋予CollectionView的collectionViewLayout属性。

UICollectionViewFlowLayout

Apple为我们提供了一个最简单可能也是最常用的默认layout对象,UICollectionViewFlowLayout。
Flow Layout简单说是一个直线对齐的layout,最常见的Grid View形式即为一种Flow Layout配置。上面的照片架界面就是一个典型的Flow Layout。

 首先一个重要的属性是itemSize,它定义了每一个item的大小。通过设定itemSize可以全局地改变所有cell的尺寸,如果想要对某个cell制定尺寸,可以使用
     -collectionView:layout:sizeForItemAtIndexPath:方法。

间隔 可以指定item之间的间隔和每一行之间的间隔,和size类似,有全局属性,也可以对每一个item和每一个section做出设定:

    @property (CGSize) minimumInteritemSpacing
    @property (CGSize) minimumLineSpacing
    -collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
    -collectionView:layout:minimumLineSpacingForSectionAtIndex:

滚动方向 由属性scrollDirection确定scroll view的方向,将影响Flow Layout的基本方向和由header及footer确定的section之间的宽度

    UICollectionViewScrollDirectionVertical
    UICollectionViewScrollDirectionHorizontal

Header和Footer尺寸 同样地分为全局和部分。需要注意根据滚动方向不同,header和footer的高和宽中只有一个会起作用。
垂直滚动时section间宽度为该尺寸的高,而水平滚动时为宽度起作用。

    @property (CGSize) headerReferenceSize
    @property (CGSize) footerReferenceSize
    -collectionView:layout:referenceSizeForHeaderInSection:
    -collectionView:layout:referenceSizeForFooterInSection:
缩进

    @property UIEdgeInsets sectionInset;
    -collectionView:layout:insetForSectionAtIndex:

总结
    一个UICollectionView的实现包括两个必要部分:UICollectionViewDataSource和UICollectionViewLayout,和一个交互部分:
    UICollectionViewDelegate。而Apple给出的UICollectionViewFlowLayout已经是一个很强力的layout方案了。

UICollectionViewLayoutAttributes

 UICollectionViewLayoutAttributes是一个非常重要的类,先来看看property列表:

     @property (nonatomic) CGRect frame
     @property (nonatomic) CGPoint center
     @property (nonatomic) CGSize size
     @property (nonatomic) CATransform3D transform3D
     @property (nonatomic) CGFloat alpha
     @property (nonatomic) NSInteger zIndex
     @property (nonatomic, getter=isHidden) BOOL hidden

可以看到,UICollectionViewLayoutAttributes的实例中包含了诸如边框,中心点,大小,形状,透明度,层次关系和是否隐藏等信息。

自定义的UICollectionViewLayout

UICollectionViewLayout的功能为向UICollectionView提供布局信息,不仅包括cell的布局信息,也包括追加视图和装饰视图的布局信息。
实现一个自定义layout的常规做法是继承UICollectionViewLayout类,然后重载下列方法:


  - (CGSize)collectionViewContentSize    返回collectionView的内容的尺寸

  -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

    返回rect中的所有的元素的布局属性
    返回的是包含 UICollectionViewLayoutAttributes的NSArray
    UICollectionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息,
    通过不同的UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollectionViewLayoutAttributes:

        layoutAttributesForItemAtIndexPath:
        layoutAttributesForSupplementaryViewOfKind:withIndexPath:
        layoutAttributesForDecorationViewOfKind:withIndexPath:

 -(UICollectionViewLayoutAttributes _)layoutAttributesForItemAtIndexPath:(NSIndexPath _)indexPath

     返回对应于indexPath的位置的cell的布局属性

 -(UICollectionViewLayoutAttributes _)layoutAttributesForSupplementaryViewOfKind:(NSString _)kind atIndexPath:(NSIndexPath *)indexPath

     回对应于indexPath的位置的追加视图的布局属性,如果没有追加视图可不重载

 -(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString_)decorationViewKind atIndexPath:(NSIndexPath _)indexPath

     返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载

 -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

    当边界发生改变时,是否应该刷新布局。如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息。

另外需要了解的是,在初始化一个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来生成更新后的布局。