陈斌彬的技术博客

Stay foolish,stay hungry

iOS UICollectionView 总结

1.最简单的UICollectionView

UICollectionView是一个使用起来比较复杂的视图,即使是最简单的实现,也必须提供数据源dataSource,和布局方法UICollectionViewLayout,才可以实现一个可见可用的视图效果。

与TableView相同,dataSource需要提供的是

  • collectionView:numberOfItemsInSection: //返回每段显示的数据个数
  • collectionView:cellForItemAtIndexPath: // 返回数据的显示cell获取cell必须使用dequeueReusableCellWithReuseIdentifier:forIndexPath:方法,与TableView不同的是,这里不会返回nil,而是必须把cell的类别注册到collectionView供此方法调用,否则会出错。

布局我们使用已经提供的UICollectionViewLayout的子类UICollectionViewFlowLayout,在初始化UICollectionView的时候传入

UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:layout];
collectionView.dataSource = self;
layout.itemSize = CGSizeMake(50, 50); //设定大小

2.UICollectionViewCell详解

UICollectionViewCell不像UITableViewCell一样提供了可用的几种样式,因此建议实现其子类来使用。 子类实现时候的几个注意点:

所有的自定义View需要加入contentView中。

实现prepareForReuse对重用cell进行必要的清理。

定义selectedBackgroundView来实现选中时候的区分背景

使用前先需要先注册:

[collectionView registerClass:[CustomCollectionViewCell class] forCellWithReuseIdentifier:@“MyIdentifier”];

对于段头或者段尾等附加显示的元素,同样需要注册:

[collectionView registerClass:[CustomCollectionViewCell class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader(Footer) withReuseIdentifier:@“HeaderIdentifier"];

并提供相应的dataSource方法:

collectionView:viewForSupplementaryElementOfKind:atIndexPath:

别忘了还需要设置layout:

对于UICollectionViewFlowLayout来说,可以使用

head(foot)erReferenceSize
或者代理方法:- collectionView:layout:referenceSizeForHead(Foot)erInSection:

cell和SupplementaryView的indexPath是分别计数的,互不干扰。SupplementaryView中的Header和Footer计数也是分开的,尽管他们需要在同一个数据方法里面实现。

3.UICollectionView的选择

UICollectionView选择的delegate方法跟UITableView的选择基本一致,这里就不详细说了,比较简单。 略有不同的是,UICollectionView没有默认highlighted的颜色样式,需要自己通过代理方法设置,cell里面是选中的颜色,而不是highlighted的颜色,区别应当注意。

4.UICollectionView的插入删除和移动

与TableView不同,UICollectionView没有默认的删除和插入,也没有代理方法,而是直接调用其类方法即可

- insertItemsAtIndexPaths:  //插入
moveItemAtIndexPath:toIndexPath:  //移动
deleteItemsAtIndexPaths:  //删除

同样可以进行批量操作。

- performBatchUpdates:completion:

5.cell的拷贝和粘贴或者自定义操作

长按cell的时候出现可以出现菜单,这个菜单是由UIMenuController实现的,它包含了剪切、拷贝、粘贴、删除等等操作,要实现这个菜单,必须实现三个方法

collectionView:shouldShowMenuForItemAtIndexPath: //是否弹出菜单,需要返回YES
-  collectionView:canPerformAction:forItemAtIndexPath:withSender: //是否可以弹出事件
假如我们只想使用拷贝和粘贴,可以这样写:
- (BOOL)collectionView:(UICollectionView *)collectionView
      canPerformAction:(SEL)action
    forItemAtIndexPath:(NSIndexPath *)indexPath
            withSender:(id)sender {
   if ([NSStringFromSelector(action) isEqualToString:@"copy:"]
        || [NSStringFromSelector(action) isEqualToString:@"paste:"])
        return YES;
    return NO;
}

- collectionView:performAction:forItemAtIndexPath:withSender: //对事件进行相应操作

6.自定义布局

当UICollectionViewFlowLayout以及其子类不能满足布局的需求时,可以创造UICollectionViewLayout的子类进行自动布局。

简单来讲,自定义布局就是需要提供所有元素的位置,随后,collectionView:cellForItemAtIndexPath等方法会根据layout来请求元素。

自定义自动布局必须需要重写的方法有:
-collectionViewContentSize 
返回collectionView的大小
-layoutAttributesForElementsInRect:
返回区域内元素的属性数组
-layoutAttributesForItemAtIndexPath: 
返回cell的布局属性
-layoutAttributesForSupplementaryViewOfKind:atIndexPath:    
返回SupplementaryView的布局属性(可选)
-layoutAttributesForDecorationViewOfKind:atIndexPath:    
返回DecorationView的布局属性(可选)
-shouldInvalidateLayoutForBoundsChange:
边界变化时是否自动布局,返回BOOL

建议写法:

首先实现方法

- (void)prepareLayout {
    此方法中计算出全部元素布局所需要的属性并以indexPath为关键字存入字典
    cell属性的生成方法:
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    SupplementaryView属性的生成方法:
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:@"customId" withIndexPath:indexPath];
    根据indexPath确定attr的frame,或者使用center和size属性来确定frame。
    把attr保存到字典中
}

- (CGSize)collectionViewContentSize {
    通过self.collectionView获取相关信息并计算大小
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    建立一个可变数组attributes
    遍历所有存储的attr
    如果frame存在于rect中,则加入数组
    if(CGRectIntersectsRect(rect, attr.frame)){
        [attributes addObject:attr];  
    }
    返回数组
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
        只需要返回存储字典里的独立属性即可。
}