collection View学习笔记

本文对比了collectionView和tableView的共性和差异,重点介绍了collectionView的布局,包括预定义的流布局和自定义布局的实现,详细讲解了如何创建UICollectionViewLayoutAttributes对象和重写相关方法。此外,还探讨了增删子视图的处理。

collection View的三个逻辑组成部分:数据、子视图、布局。


一、collectionView和tableView的比较

1. 和tableView的共同点:

(1)都继承scrollView
(2)都需要两个代理:delegate和dataSourceDelegate
(3)都按分区(section)和单元格(cell)对子视图进行管理
(4)都有重用机制,每次从重用池中取出一个cell都会执行对应类的layoutSubviews方法。执行这个方法的时候,已经更新了cell的大小,但不会更新cell的contentView的大小。不论对于cell还是cell的contentView,默认frame的大小和bounds的大小都是一样的。

2. 和tableView的不同点:

tableViewcollectionView
(1)初始化tableView初始化的时候需要指定表格的style;collectionView初始化的时候需要指定一个布局类的对象。
(2)管理子视图布局tableView按行来管理子视图,一行对应一个cell。布局只涉及行高;collectionView子视图可以不受行和列的约束自由分布,一个item对应一个cell。由布局类UICollectionViewLayout类来指定视图的位置。
(3)重用机制tableView的重用机制是针对cell的,且不是强制的,即某种cell类型可以在未注册的情况下使用;collectionView的重用机制面向所有子视图,包括section、cell、装饰性的子视图,且是强制的,所有用到的cell类型和section类型都需要注册之后才可以使用。
(4)重用机制调用的初始化方法在tableView的重用机制里,对于注册了的类,会在需要创建该类的对象的时候自动调用该类的initWithStyle:reuseIdentifier:方法;在collectionView的重用机制里,对于注册了的类,会在需要该类的对象的时候自动调用该类的initWithFrame:方法。

二、collectionView的布局

由于已经学过tableView,类似的collectionView的协议方法都能明白。因此这里把重点放在item的布局上。
(1)collectionView何时需要布局:
collectionView加载的时候,在增删、移动子视图的时候会需要布局信息。
(2)collectionView如何指定布局样式
collectionView在初始化的时候就指定了collectionViewLayout对象。选择不同的布局方法,也就是给collectionView指定不同的collectionViewLayout。
(3)布局样式(layout对象)怎么给collectionView工作
在collectionView第一次加载或者大小被调整的时候就会去向它的layout对象索要布局信息。也可以手动调用invalidateLayout方法来强制layout对象重新产生布局信息给collectionView。
(4)布局样式的分类
按照是否自定义,划分起来只有两种:一是苹果预定义的流布局,二是自定义布局。

1. 流布局

流布局是苹果预先定义的布局,这种布局就好比流水一样,将一个个cell按顺序排列。出来的效果跟网格差不多。
涉及的类和协议:
类:UICollectionViewFlowLayout
协议:UICollectionViewDelegateFlowLayout

(1)UICollectionViewFlowLayout类

这个类继承UICollectionViewLayout。流布局的基本属性:
minimumLineSpacing最小行距
minimumInterItemSpacing最小左右间距
headerReferenceSize分区头的大小
footerReferenceSize分区为的大小
itemSize单元格cell的大小
sectionInset分区的内边距(四个边缘的内边距)
scrollDirection滚动方向
直接设置这些属性,可以对所有的cell起作用,也就是所有的分区头大小都是headerReferenceSize,所有item的大小都是itemSize,以此类推。因此出来的效果就是等间距的网格。如果想要不同的行或者不同的分区又不一样的尺寸,可以利用UICollectionViewDelegateFlowLayout协议的方法。看下一点。

(2)UICollectionViewDelegateFlowLayout协议

这个协议继承UICollectionViewDelegate协议,里面定义了若干与流布局相关的可选方法。这些方法就是对上面类出的属性进行设置(属性作为方法的返回值),参数中带了indexPath,以此来区别不同的section和item。和tableView、collectionView两个代理的协议方法的使用很类似,这里不多说。
由于这个协议本身也遵循UICollectionViewDelegate协议,因此遵循了UICollectionViewDelegateFlowLayout协议之后可以不用再重复遵循UICollectionViewDelegate协议了。

2、自定义布局

自定义布局就是自己写一个布局类,供collectionView使用。

(1) 涉及的类及它们的角色or职责:

把collectionView比作房子,把子视图比作家具。
一个家具的位置:UICollectionViewLayoutAttributes保存单个子视图的布局信息。
集中家具的位置信息,计算房子大小:UICollectionViewLayout负责产生UICollectionViewLayoutAttributes对象和计算collectionView的滚动区域的大小。
摆家具:UICollectionView进行子视图的布局。

(2)开始写布局类

继承UICollectionViewLayout类,自己写一个布局类,在类中重写三个方法:
-(void)prepareLayout;
-(CGSize)collectionViewContentSize;
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
这个三个方法是依次执行的,执行完这三个方法,就生成了加载collectionView所需要的所有布局信息。这三个方法要完成的工作:

  • 通常在prepareLayout里完成各种计算以确定每个子视图的位置、大小等布局信息(并把每个子视图的布局信息封装成一个UICollectionViewLayoutAttributes对象)。
  • 在collectionViewContentSize中返回collectionView的滚动区域大小,也就是其contentView的大小。
  • 在layoutAttributesForElementsInRect:方法中返回UICollectionViewLayoutAttributes对象数组,该数组包含了collectionView里所有子视图的UICollectionViewLayoutAttributes对象。
①在prepareLayout方法中根据数据计算item的frame

通常,item的大小和位置是需要根据数据内容计算得出的,这就涉及到获取数据的问题。然而加载数据源和解析数据不应该由布局类来完成,这些都应该交给管理collectionView的controller去做。controller作为collectionView的代理,在实现代理方法的时候,已经将数据转换为相关的尺寸。在布局类的prepareLayout方法里,应该直接调用相关的协议方法来获取需要的尺寸。如果现有的协议方法还不能满足需求,可以自定义一个布局协议,协议的方法也应该由管理collectionView的controller来实现。

②创建UICollectionViewLayoutAttributes对象

item与布局有关的属性需要存储在UICollectionViewLayoutAttributes对象里。创建UICollectionViewLayoutAttributes对象的方法有:
layoutAttributesForCellWithIndexPath:
layoutAttributesForSupplementaryViewOfKind:withIndexPath:
layoutAttributesForDecorationViewOfKind:withIndexPath:
其中,要创建一般的item的布局属性就用第一种方法;创建分区头、分区尾的布局属性用第二中方法;其他装饰性的view的布局属性用第三种方法。
item的布局属性对象可以在prepareLayout方法中就创建,也可以在layoutAttributesForElementsInRect方法中才创建。

③除了上述三个方法以外还可能需要重写的方法

collectionView每次加载,都会顺序执行上面三个方法。上面三个方法为collectionView的加载提供了所有子控件的布局信息。
除了加载collectionView的时候需要所有子视图的布局信息,有时候collectionView也会需要单个子视图的布局属性,比如说在动态增减、移动子视图的时候。此时,collectionView就会调用一下三个方法来获取某个子视图的布局属性:
layoutAttributesForItemAtIndexPath:
layoutAttributesForSupplementaryViewOfKind:atIndexPath:
layoutAttributesForDecorationViewOfKind:atIndexPath:
这三个方法都返回单个子视图的布局属性,分别是针对普通item、分区头尾、装饰性视图的布局属性。因此可以根据需求,选择性地来重写。如果完全不需要支持子视图的增减、移动等操作,就不需要重写。

(3)自定义布局类中可能会有的属性(仅供参考)

@property (nonatomic,assign)CGSize itemSize;
@property(nonatomic,assign)NSUInteger numberOfColumns;
@property(nonatomic,assign)NSUInteger numberOfLines;
@property(nonatomic,assign)CGFloat itemSpacing;
@property(nonatomic,assign)CGFloat lineSpacing;
@property(nonatomic,assign)UIEdgeInsets sectionInsets;
……

(4)苹果给出的一些让collectionView更好看的建议

①cell的类型只用一种,用来展示数据内容。
②用分区头和分区尾来强调cell所展示的内容。类型可以有多重,需要是UICollectionReusableView的子类,以符合重用机制。
③用装饰性的视图增加美观性。装饰性视图的类型可以有多种,应该与数据无关,只是单纯的装饰物。因此外观固定,在用时只需要指定位置,不需要额外配置。确定装饰性视图的位置可以用上Z轴坐标,来确定视图是作为背景还是能遮盖部分的cell。装饰性视图也需要是UICollectionReusableView的子类,以符合重用机制。当然也可以直接从xib文件加载。
④重写 -(UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath方法,返回一个item的初始化布局属性(位置、大小、透明度等等)。在item初始化的时候会按照这个方法返回的attributes来出现在contentView上,然后会有动画效果让它最终转以layoutAttributesForItemAtIndexPath:方法返回的attribute的来布局。
⑤如果在更新某一个item的时候,所有的item都呈现出动画效果,而只想让更新的那个item有动画效果,就重写-(void)prepareForCollectionViewUpdates:(NSArray<UICollectionViewUpdateItem *> *)updateItems方法。这个方法会在collectionView发生变化的时候有collectionView自动调用的,并且会把collectionView即将发生的变化传进这个方法中。因此重写这个方法,在方法体中就可以获取collectionView发生的变化,即CollectionViewUpdateItem对象,以便在上面的方法中加以判断,只针对发生变化的item返回初始化布局属性。

三、增、删、重新加载、移动子视图

增、删、重新加载、移动子视图的方法:

- (void)insertSections:(NSIndexSet *)sections;
- (void)deleteSections:(NSIndexSet *)sections;
- (void)reloadSections:(NSIndexSet *)sections;
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
(void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;

这些方法默认都是会触发动画的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值