UICollectionView详解
2017-02-15 14:26
260 查看
标准的UICollectionView包含三个部分,它们都是UIView的子类:
Cells 用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容,这个稍后再说
Supplementary Views 追加视图 如果你对UITableView比较熟悉的话,可以理解为每个Section的Header或者Footer,用来标记每个section的view
Decoration Views 装饰视图 这是每个section的背景
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/e6ddffff06d878dcd6e6bd30bb83b7c3.png)
某个section里有多少个item -collectionView: numberOfItemsInSection:
对于某个位置应该显示什么样的cell -collectionView: cellForItemAtIndexPath:
Supplementary View的方法(section头部视图,section尾部视图)
collectionView: viewForSupplementaryElementOfKind: atIndexPath:
-collectionView:shouldHighlightItemAtIndexPath: 是否应该高亮?
-collectionView:didHighlightItemAtIndexPath: 如果1返回结果为是,那么高亮
-collectionView:shouldSelectItemAtIndexPath: 无论1结果如何,都询问是否可以被选中?
-collectionView:didUnhighlightItemAtIndexPath: 如果1返回结果为是,那么现在取消高亮
-collectionView:didSelectItemAtIndexPath: 如果3返回结果为是,那么选中cell
@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 是否隐藏
注:在初始化一个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来生成更新后的布局。
示例:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/d260b49c00402857a0594607e43f9443.gif)
// 布局.m文件
UICollectionView头视图实现:
也是通过注册来创建的:
自定义头部视图的类必须继承UICollectionReusableView
头部或者尾部的数据源方法:
Cells 用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容,这个稍后再说
Supplementary Views 追加视图 如果你对UITableView比较熟悉的话,可以理解为每个Section的Header或者Footer,用来标记每个section的view
Decoration Views 装饰视图 这是每个section的背景
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/e6ddffff06d878dcd6e6bd30bb83b7c3.png)
UICollectionViewDataSource(与数据相关)
section的数量 -numberOfSectionsInCollection:某个section里有多少个item -collectionView: numberOfItemsInSection:
对于某个位置应该显示什么样的cell -collectionView: cellForItemAtIndexPath:
Supplementary View的方法(section头部视图,section尾部视图)
collectionView: viewForSupplementaryElementOfKind: atIndexPath:
UICollectionViewDelegate(与数据无关)
关于用户交互,UICollectionView也做了改进。每个cell现在有独立的高亮事件和选中事件的delegate,用户点击cell的时候,现在会按照以下流程向delegate进行询问:-collectionView:shouldHighlightItemAtIndexPath: 是否应该高亮?
-collectionView:didHighlightItemAtIndexPath: 如果1返回结果为是,那么高亮
-collectionView:shouldSelectItemAtIndexPath: 无论1结果如何,都询问是否可以被选中?
-collectionView:didUnhighlightItemAtIndexPath: 如果1返回结果为是,那么现在取消高亮
-collectionView:didSelectItemAtIndexPath: 如果3返回结果为是,那么选中cell
UICollectionViewLayout(布局)
flowLayout的属性:// 1、一个cell(item)对应一个UICollectionViewLayoutAttributes对象 // 2、UICollectionViewLayoutAttributes对象决定了cell的frame // 首先一个重要的属性是itemSize,它定义了每一个item的大小。通过设定itemSize可以全局地改变所有cell的尺寸,如果想要对某个cell制定尺寸,可以使用-collectionView: layout: sizeForItemAtIndexPath:方法。 // 间隔 可以指定item之间的间隔和每一行之间的间隔,和size类似,有全局属性,也可以对每一个item和每一个section做出设定: * @property (CGSize) minimumInteritemSpacing 设置每行内部item之间的间距 * @property (CGSize) minimumLineSpacing 设置每行之间的间距 * -collectionView:layout:minimumInteritemSpacingForSectionAtIndex: * -collectionView:layout:minimumLineSpacingForSectionAtIndex: // 滚动方向 由属性scrollDirection确定,枚举值如下 * UICollectionViewScrollDirectionVertical * UICollectionViewScrollDirectionHorizontal // Header和Footer尺寸 同样地分为全局和部分。需要注意根据滚动方向不同,header和footer的高和宽中只有一个会起作用。垂直滚动时section间宽度为该尺寸的高,而水平滚动时为宽度起作用,如图。 * @property (CGSize) headerReferenceSize Header尺寸 * @property (CGSize) footerReferenceSize Footer尺寸 * -collectionView:layout:referenceSizeForHeaderInSection: * -collectionView:layout:referenceSizeForFooterInSection: // 缩进(也就是section的内边距) * @property UIEdgeInsets sectionInset; * -collectionView:layout:insetForSectionAtIndex: sectionHeadersPinToVisibleBounds:设置是否当元素超出屏幕之后固定头部视图位置,默认NO sectionFootersPinToVisibleBounds:设置是否当元素超出屏幕之后固定尾部视图位置,默认NO
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 是否隐藏
自定义UICollectionViewLayout
常规做法是继承UICollectionViewLayout类,然后重载下列方法: // 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局 * - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds // 初始化布局 * - (void)prepareLayout * - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect /* 返回rect中的所有的元素的布局属性 * 返回的是包含UICollectionViewLayoutAttributes的NSArray * UICollectionViewLayoutAttributes可以是cell、追加视图或装饰视图的信息,通过不同的 * UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollectionViewLayoutAttributes: */ // 返回对应于indexPath的位置的cell的布局属性 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath // 返回对应于indexPath的位置的追加视图的布局属性,如果没有追加视图可不重载 - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath // 返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载 - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath // 返回collectionView的内容的尺寸 - (CGSize)collectionViewContentSize // 这个方法的返回值,就决定了collectionView停止滚动时的偏移量 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
注:在初始化一个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来生成更新后的布局。
布局之间的切换
直接更改collectionView的collectionViewLayout属性可以立即切换布局。而如果通过setCollectionViewLayout:animated:,则可以在切换布局的同时,使用动画来过渡。对于每一个cell,都将有对应的UIView动画进行对应示例:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201711/d260b49c00402857a0594607e43f9443.gif)
// 布局.m文件
#import "LayoutController.h" @implementation LayoutController - (instancetype)init{ if (self = [super init]) { } return self; } /** * 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局 * 一旦重新刷新布局,就会重新调用下面的方法: 1.prepareLayout 2.layoutAttributesForElementsInRect:方法 */ - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } // 初始化布局 - (void)prepareLayout{ [super prepareLayout]; // 水平滚动 self.scrollDirection = UICollectionViewScrollDirectionHorizontal; // 设置section的内边距 CGFloat mid = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5; self.sectionInset = UIEdgeInsetsMake(0, mid, 0, mid); } /** UICollectionViewLayoutAttributes *attrs; 1.一个cell对应一个UICollectionViewLayoutAttributes对象 2.UICollectionViewLayoutAttributes对象决定了cell的frame */ /** * 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性) * 这个方法的返回值决定了rect范围内所有元素的排布(frame) */ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ // 获得super已经计算好的布局属性 NSArray *array = [super layoutAttributesForElementsInRect:rect]; // 计算collectionView的中心点的x CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5; for (UICollectionViewLayoutAttributes *attribute in array) { // 计算缩放比 CGFloat delta = ABS(attribute.center.x - centerX); // 如果两个中心点重叠的话缩放比就为0,所以要用1减一下 CGFloat scale = 1 - delta / self.collectionView.frame.size.width; // 设置缩放比例 attribute.transform = CGAffineTransformMakeScale(scale, scale); } return array; } /** * 这个方法的返回值,就决定了collectionView停止滚动时的偏移量 */ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{ // 计算出最终显示的矩形框 CGRect rect; rect.origin.x = proposedContentOffset.x; rect.origin.y = 0; rect.size = self.collectionView.frame.size; // 获得super已经计算好的布局属性 NSArray *array = [super layoutAttributesForElementsInRect:rect]; // 计算collectionView最中心点的x值 CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5; // 计算最小的间距值 CGFloat min = MAXFLOAT; for (UICollectionViewLayoutAttributes *attrs in array) { if (ABS(min) > ABS(attrs.center.x - centerX)) { min = attrs.center.x - centerX; } } proposedContentOffset.x += min; return proposedContentOffset; } @end
UICollectionViewLayoutAttributes类:配置collectionView的布局
//配置item的布局位置 @property (nonatomic) CGRect frame; //配置item的中心 @property (nonatomic) CGPoint center; //配置item的尺寸 @property (nonatomic) CGSize size; //配置item的3D效果 @property (nonatomic) CATransform3D transform3D; //配置item的bounds @property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0); //配置item的旋转 @property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0); //配置item的alpha @property (nonatomic) CGFloat alpha; //配置item的z坐标 @property (nonatomic) NSInteger zIndex; // default is 0 //配置item的隐藏 @property (nonatomic, getter=isHidden) BOOL hidden; //item的indexpath @property (nonatomic, strong) NSIndexPath *indexPath; //获取item的类型 @property (nonatomic, readonly) UICollectionElementCategory representedElementCategory; typedef NS_ENUM(NSUInteger, UICollectionElementCategory) { UICollectionElementCategoryCell, (item) UICollectionElementCategorySupplementaryView, (类似于tableView的session头,尾) UICollectionElementCategoryDecorationView }; @property (nonatomic, readonly, nullable) NSString *representedElementKind; UIKIT_EXTERN NSString *const UICollectionElementKindSectionHeader NS_AVAILABLE_IOS(6_0); 头 UIKIT_EXTERN NSString *const UICollectionElementKindSectionFooter NS_AVAILABLE_IOS(6_0); 尾 //一些创建方法 + (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath; + (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath; + (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;
UICollectionView头视图实现:
也是通过注册来创建的:
[self.collectionV registerNib:[UINib nibWithNibName:NSStringFromClass([CKCollectionHeadView class]) bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"collectionVHeader"]; CKCollectionHeadView -- 自定义的类
自定义头部视图的类必须继承UICollectionReusableView
头部或者尾部的数据源方法:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{ if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { // 头部 CKCollectionHeadView *headView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"collectionVHeader" forIndexPath:indexPath]; headView.VC = self; headView.power = [NSString stringWithFormat:@"%ld", self.fighting]; headView.imageUrlArray = self.imageUrlArray; headView.imageArray = self.imageArray; return headView; }else { return nil; } } // kind:UICollectionElementKindSectionFooter 尾部
相关文章推荐
- 转 UICollectionView 详解
- UICollectionViewLayout详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView 的使用详解
- UICollectionView详解1
- UICollectionView详解二
- UICollectionView详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView 集合视图详解
- iOS开发- UICollectionView详解
- iOS UICollectionView详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView详解
- UICollectionView详解三:UICollectionViewLayout
- UICollectionViewAPI详解