概述:
UICollectionViewController 类似于 UITableViewController 是UICollectionView 与 控制器类的整合封装.其作用也就主要体现在UICollocationView
UICollectionViewController 基本属性
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout |
@property(nonatomic, retain) UICollectionView *collectionView |
@property(nonatomic, readonly) UICollectionViewLayout *collectionViewLayout |
@property(nonatomic) BOOL clearsSelectionOnViewWillAppear |
@property(nonatomic, assign) BOOL useLayoutToLayoutNavigationTransitions |
UICollectionView: 是一种新的数据展示方式, 简单来说可以把他理解成多列的UITableView.
UICollectionView概述:
标准的 UICollectionView 包含三个部分,他们都是UIView的子类:
- Cells 用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容,这个稍后再说
- Supplementary Views 追加视图 如果你对UITableView比较熟悉的话,可以理解为每个Section的Header或者Footer,用来标记每个section的view
- Decoration Views 装饰视图 这是每个section的背景,比如iBooks中的书架就是这个----不管一个UICollectionView的布局如何变化,这三个部件都是存在的。
关于 UICollectionView 的实现:
实现一个UICollectionView 和实现一个UITableView基本上没有什么大的区别.
他们都是同样的datasource 和 delegate 设计模式: datasource为view提供资源, 告诉view要显示些什么以及如何显示他们, delegate提供一些样式的小细节以及用户交互的响应.
同样有三个必须实现的协议方法:通过协议方法进行两个视图子类(cells/ supplementary views)的展示.
需要注意的是, 对于Decoration views, 提供方法并不在UIControllerViewDataSource中, 而是直接在UICollectionViewLayout类中的(因为它仅仅于视图相关, 而与数据无关).
同样具有重用机制. 但是需要注意的时,在UICollectionView中, 不仅cell可以重用, Supplement View 和 Decoration view 也可以并且应该被重用的.
关于 UICollectionView 的代理:
UICollectionViewDelegate: View的外形, 用户交互等由UICollectionViewDelegate来负责, 而数据的提供则有UICollectionViewDataSource负责.
关于Cell:
相对于UITableViewCell, UICollectionViewCell则相对简单,这主要是由于展示对象的性质决定的, 因为UICollectionView展示的对象更为灵活,因此需求也就千奇百怪. 因此SDK提供给我们的默认UICollectionViewCell在结构上比较简单, 由上至下: cell本身的View/ backgroundView/ selectedBackgroundView/
contentView.
UICollectionViewLayout:
UICollectionView的精髓.可以通过对其子类化实现对单元格的自定义布局, 这也就是UICollectionView 和 UITableView的最大区别了. UICollectionViewLayout可以说时UICollectionView的大脑和中枢, 它负责cell Supplementary View 和 Decoration Views 进行组织, 为他们设定各自的属性,
包括但不限于: 位置/ 尺寸/ 透明度/ 层级关系/ 形状/ 等等....
Apple 为我们提供了一个最简答可能也是最常用的默认的layout对象, UICollectionViewFlowLayout. Flow Layout 简单说就是一个直线对其的layout. 通过对这个子类的子类化可以实现大部分的瀑布流类的简单实现.
总结:
总的来说, 一个UICollectionView的实现必须包含两个必要的部分: UICollectionViewDataSource 和 UICollectionViewLayout分别提供展示的View内容和 展示的View的位置. 和一个交互部分: UICollectionViewDelegate. 而 Apple给出的UICollectionViewFlowLayout已经是一个很强力的layout方案了.但是仅仅如此还是不够的.
UICollectionView的强大之处, 就在于各种layout的自定义实现, 以及他们之间的切换.
1. UICollectionView
1.1 基本属性/方法
代理
|
背景视图
|
重用方法
|
- (void)setCollectionViewLayout:(UICollectionViewLayout *) animated:(BOOL)
animated 切换布局
|
- (void)reloadData
数据更新
|
- (NSInteger)numberOfSections
- (NSInteger)numberOfItemsInSection:(NSInteger)
section 获取内容数量
|
添加/移动/删除items(类似tableView的增删改)
|
- (void)moveSection:(NSInteger)
section toSection:(NSInteger)
newSection 添加/删除/移动sections
|
@property(nonatomic) BOOL allowsSelection
@property(nonatomic) BOOL allowsMultipleSelection
animated:(BOOL)
animated scrollPosition:(UICollectionViewScrollPosition)
scrollPosition - (void)deselectItemAtIndexPath:(NSIndexPath *) animated:(BOOL)
animated 是否可选
|
对item/section进行定位
|
获取布局信息
|
- (void)performBatchUpdates:(void
(^)(void))
updates completion:(void
(^)(BOOL finished))
completion 多点选择 的删除于移动
|
1.2 常用操作
添加/删除/移动/编辑
1.3 UICollectionViewDataSource/ UICollectionViewDelegate 协议
UICollectionViewDataSource | |
选中状态历程
| |
- (void)collectionView:(UICollectionView *)
collectionView willDisplayCell:(UICollectionViewCell *)cell - (void)collectionView:(UICollectionView *)
collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind - (void)collectionView:(UICollectionView *)
collectionView didEndDisplayingCell:(UICollectionViewCell *)cell - (void)collectionView:(UICollectionView *)
collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind 监控单元格的加载于移除
|
- (UICollectionViewTransitionLayout *)collectionView:(UICollectionView*)
collectionView transitionLayoutForOldLayout:(UICollectionViewLayout*)fromLayout |
编辑菜单
|
- (BOOL)collectionView:(UICollectionView *) withSender:(id)
sender withSender:(id)
sender 当编辑菜单存在时可用
|
1.4 实例
//1 创建用于布局单元格的布局对象
UICollectionViewFlowLayout *flowLayout1 = [[UICollectionViewFlowLayoutalloc]init]; //2 设置最小行间距 flowLayout1.minimumInteritemSpacing= 5; //3 设置最小列间距 flowLayout1.minimumLineSpacing= 5; //4 设置单元格的大小 flowLayout1.itemSize = CGSizeMake((CGRectGetWidth(self.view.bounds) - 15) /2 , (CGRectGetWidth(self.view.bounds) - 15) /2); //5 设置布局时的内边距 flowLayout1.sectionInset = UIEdgeInsetsMake(5,5,5,5); //使用自定义的layout布局 WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init]; flowLayout.columnCount = 2;//表示有两列 flowLayout.delegate = self; flowLayout.sectionInset = UIEdgeInsetsMake(10,10,10,10); flowLayout.itemWitdth = (CGRectGetWidth(self.view.bounds) - 30 ) / 2; UICollectionView *collectionView = [[UICollectionViewalloc]initWithFrame:self.view.boundscollectionViewLayout:flowLayout]; collectionView.tag = 1001; //设置背景颜色 collectionView.backgroundColor= [UIColorwhiteColor]; //设置代理 collectionView.delegate = self; collectionView.dataSource = self; [self.viewaddSubview:collectionView]; [collectionView release]; [flowLayout release]; //为对应重用标示注册单元格类型 [collectionView registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:@"CELL"];
//执行协议方法
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView
{
return 1; } //返回制定分区的单元格数量 - (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section { return self.datasource.count; } //返回对应行的单元格对象 - (UICollectionViewCell *)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath { ImageCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CELL"forIndexPath:indexPath]; cell.backgroundColor = [UIColor colorWithRed:arc4random() %256 / 255.0 green:arc4random() %256 / 255.0 blue:arc4random() %256 / 255.0 alpha:1]; ImageModel *model = self.datasource[indexPath.item]; [cell.imageView setImageWithURL:[NSURLURLWithString:model.thumbURL]]; return cell; } |
2. UICollectionViewLayout
集合视图控制器布局对象完成了对集合视图控制器对象单元格frame的布局,所以其核心即单元格frame的布局,最终要求即获得一个单元格属性模型数组.
2.1 基本属性
@property(nonatomic, readonly) UICollectionView *collectionView
- (CGSize)collectionViewContentSize
当前使用此布局对象的集合视图
同时还提供了相应的cell布局的切换/ cell的增/删/移动
|
2.2 自定义
自定义布局时, 必须要重写的方法如下:
prepareLayout (即将开始布局时调用.重写此方法, 实现在即将布局时,获取属性模型数组)
collectionViewContentSize(重写此方法返回collectionView的contentSize)
layoutAttributesForSupplementaryViewOfKind:atIndexPath:
(if your layout supports supplementary views)
layoutAttributesForDecorationViewOfKind:atIndexPath:
(if your layout supports decoration views) shouldInvalidateLayoutForBoundsChange:
如果对应的collection需要实现增加/删除/移动功能, 则需要重写以下方法
-
initialLayoutAttributesForAppearingItemAtIndexPath:
-
initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:
-
initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
-
finalLayoutAttributesForDisappearingItemAtIndexPath:
-
finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:
-
finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:
- 另外视图布局对象也提供了一些方法用于提升性能
-
UICollectionViewLayout的自定义大概可以分为一下几步:
1> 根据需求扩展UICollectionView的协议方法
2> 定义需求的属性信息(因为UICollectionViewLayout基类的属性)
3> 获取cell的的属性模型数组(自定义的核心)
4> 重写相应的父类方法(基本状态下有: )
2.4: 自定义实例(瀑布流)
#import
<UIKit/UIKit.h>
@class WaterFlowLayout; //声明扩展协议 @protocol WaterFlowLayoutDelegate <UICollectionViewDelegate> //为布局对象返回单元格的最终高度 - (CGFloat)collectionView:(UICollectionView*)collectionView layout:(WaterFlowLayout*)waterFlowLayout heightyForItemAtIndexPath:(NSIndexPath*)indexPath; @end @interface WaterFlowLayout : UICollectionViewLayout @property (nonatomic,assign)id<WaterFlowLayoutDelegate>delegate;//代理对象属性 @property (nonatomic,assign)NSUInteger columnCount;//设置瀑布流的列数 @property (nonatomic,assign)CGFloat itemWitdth; //设置瀑布流中每个单元格的宽度 @property (nonatomic,assign)UIEdgeInsets sectionInset;//设置瀑布流的内边距 @end
|
#import"WaterFlowLayout.h"
@interfaceWaterFlowLayout
()
@property(nonatomic,assign)NSUInteger
numberOfItems;//总的单元格数量
@property (nonatomic,assign)CGFloat interItemSpacing;//列间距 @property (nonatomic,retain)NSMutableArray *columnHeights;//保存布局时当前每一列的当前总高度. 有多少列,就有多少个数组元素 @property (nonatomic,retain)NSMutableArray *itemAttributes;//用于保存每一个单元格的属性模型对象. - (NSInteger)shortestColumnIndex;//返回columnHeights数组中当前最短列下标
- (NSInteger)longestColumnIndex;//返回columnHeightss数组中当前最长列下标
@end
@implementationWaterFlowLayout
- (void)dealloc { [_columnHeights release]; [_itemAttributes release]; [super dealloc]; } - (NSMutableArray *)columnHeights { if (!_columnHeights) { self.columnHeights= [NSMutableArrayarray]; } return _columnHeights; } - (NSMutableArray *)itemAttributes { if (!_itemAttributes) { self.itemAttributes= [NSMutableArrayarray]; } return _itemAttributes; } - (NSInteger)shortestColumnIndex { NSInteger index = 0; CGFloat shortestHeight = CGFLOAT_MAX;//浮点数的最大值 for (int i = 0 ; i < self.columnHeights.count;i++) { CGFloat currentHeight = [self.columnHeights[i]doubleValue]; if (currentHeight < shortestHeight) { index = i; shortestHeight = currentHeight; } } return index; } - (NSInteger)longestColumnIndex { NSInteger indext = 0; CGFloat longestHeight = 0; for (int i = 0; i <self.columnHeights.count; i ++) { CGFloat currentHeight = [self.columnHeights[i]doubleValue]; if (currentHeight > longestHeight) { indext = i; longestHeight = currentHeight; } } return indext; } - (void)layoutItems { //获取单元格的总数量 self.numberOfItems= [self.collectionViewnumberOfItemsInSection:0]; //计算内容区域的最终宽度 CGFloat contentWidth = CGRectGetWidth(self.collectionView.bounds) - self.sectionInset.left- self.sectionInset.right; //计算列边距 self.interItemSpacing= (contentWidth - self.columnCount* self.itemWitdth) / (self.columnCount- 1); //先通过列数为columnHeight 添加当前高度 for (int i = 0 ; i < self.columnCount; i ++) { //语法糖 自动转数据为对象类型 self.columnHeights[i] =@(self.sectionInset.top); } for (int i = 0 ; i < self.numberOfItems; i ++) { //创建单元格对应的indexPath对象 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; //通过代理对象得到单元格的最终高度 CGFloat itemHeight = [self.delegatecollectionView:self.collectionViewlayout:selfheightyForItemAtIndexPath:indexPath]; //获取当前最小列的下标 NSInteger shortestIndex = [self shortestColumnIndex]; //计算单元格的x, y 轴坐标 CGFloat x = self.sectionInset.left+ (self.itemWitdth +self.interItemSpacing) * shortestIndex ; CGFloat y = [self.columnHeights[shortestIndex]doubleValue] ; //创建属性模型对象 UICollectionViewLayoutAttributes*attributes = [UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath]; //设置frame attributes.frame = CGRectMake(x, y,self.itemWitdth, itemHeight); //保存在属性数组中 [self.itemAttributesaddObject:attributes]; //更新当前列(最短列)的总高度 self.columnHeights[shortestIndex] =@(y + itemHeight +self.interItemSpacing); NSLog(@"%@",NSStringFromCGRect(attributes.frame)); } } - (void)prepareLayout { [super prepareLayout]; //在准备布局时调用此方法计算每个单元格的frame [self layoutItems]; } - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { //为当前区域返回对应保存属性模型的数组 return self.itemAttributes; } - (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath { //为每个单元格分发对应的属性模型的对象 return self.itemAttributes[indexPath.item]; } - (CGSize)collectionViewContentSize { CGSize contentSize = self.collectionView.frame.size; //获取最长列的下标 NSInteger longestIndex = [self longestColumnIndex]; contentSize.height = [self.columnHeights[longestIndex]doubleValue]; return contentSize; } @end
|
2.3 UICollectionViewFlowLayout
提供了一个简单的集合视图布局, 能够实现自定义行数的简单布局. 通过itemSize/ 行间距/ 列间距/ 内边距实现单的布局
3. UICollectionViewCell
只具有四个五个属性提供了自定义空间(contentView/backgroundView/selectedBackgroundView)
和选择状态(selected/ highlighted)
4. UICollectionViewLayoutAttributes
用于保存属性的模型类,适用于UICollectionViewLayout的初始化方法为 +(id)layoutAttributesForCellWithIndexPath:(NSIndexPath *) indexPath
可以保存的属性主要有: frame/bounds/center/size/transform3D/transform/alpha/zIndex(堆叠顺序)/hidden