UICollectionView之因此强大,是由于其具备自定义功能,这一自定义就不得了啦,自由度很是大,定制的高,因此功能也是灰常强大的。本篇博客就不使用自带的流式布局了,咱们要自定义一个瀑布流。自定义的瀑布流能够配置其参数: 每一个Cell的边距,共有多少列,Cell的最大以及最小高度是多少等。git
先来看一下不一样配置参数下运行后的效果吧,每张截图的列数和Cell之间的边距都有所不一样,瀑布流的列数依次为2,3,8。有密集恐惧证的童鞋就不要看这些运行效果图了,真的会看晕的。下面这些运行效果就是修改不一样的配置参数来进行布局的。看图吧,关于瀑布流的效果就不啰嗦了。如下的效果就是使用自定义布局作的,接下来将会介绍一下其实现原理。github
在介绍上述效果实现原理以前,须要介绍一下UICollectionViewLayout。UICollectionView的自定义功能就是本身去实现UICollectionViewLayout的子类,而后重写相应的方法来实现Cell的布局,先介绍一下须要重写的方法,而后再此方法上进行应用实现上述瀑布流。好,废话少说,干活走起。数组
当布局首次被加载时会调用prepareLayout函数,见名知意,就是预先加载布局,在该方法中能够去初始化布局相关的数据。该方法相似于视图控制器的ViewDidLoad方法,稍后回用到该方法。bash
1 // The collection view calls -prepareLayout once at its first layout as the first message to the layout instance.
2 // The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information.
3 // Subclasses should always call super if they override.
4 - (void)prepareLayout;
复制代码
下方是定义ContentSize的方法。该方法会返回CollectionView的大小,这个方法也是自定义布局中必须实现的方法。说白了,就是设置ScrollView的ContentSize,即滚动区域。dom
1 // Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size to facilitate scrolling.
2 - (CGSize)collectionViewContentSize;
复制代码
下方四个方法是肯定布局属性的,下方第一个方法返回一个数组,该数组中存放的是为每一个Cell绑定的UICollectionViewLayoutAttributes属性,便于在下面第二个方法中去定制每一个Cell的属性。第三个方法就是根据indexPath来获取Cell所绑定的layoutAtrributes, 而后去更改UICollectionViewLayoutAttributes对象的一些属性并返回,第四个是为Header View或者FooterView来定制其对应的UICollectionViewLayoutAttributes,而后返回。ide
1 // UICollectionView calls these four methods to determine the layout information.
2 // Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
3 // Additionally, all layout subclasses should implement -layoutAttributesForItemAtIndexPath: to return layout attributes instances on demand for specific index paths.
4 // If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
5 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect
6 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
7 - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
8 - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;
复制代码
下方是UICollectionViewLayoutAttributes经常使用的属性,你能够在上面第二个方法中去为下方这些属性赋值,为Cell定制属于本身的Attributes。由下方的属性就对自定义布局的的强大,在本篇博客中只用到了下方的一个属性,那就是frame。函数
1 @property (nonatomic) CGRect frame;
2 @property (nonatomic) CGPoint center;
3 @property (nonatomic) CGSize size;
4 @property (nonatomic) CATransform3D transform3D;
5 @property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
6 @property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
7 @property (nonatomic) CGFloat alpha;
8 @property (nonatomic) NSInteger zIndex; // default is 0
9 @property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
复制代码
通过上面的简单介绍,想必对UICollectionViewLayout有必定的了解吧,UICollectionViewLayout中还有好多方法,之后用到的时候在给你们介绍。接下来要使用自定义布局来实现瀑布流。咱们须要在UICollectionViewLayout的子类中实现相应的布局方法,由于UICollectionViewLayout是虚基类,是不能直接被实例化的,因此咱们须要新建一个布局类,这个布局类继承自UICollectionViewLayout。而后去实现上述方法,给每一个Cell定制不一样的UICollectionViewLayoutAttributes。好了仍是拿代码说话吧。布局
1 #pragma mark -- <UICollectionViewLayout>虚基类中重写的方法
2
3 /**
4 * 该方法是预加载layout, 只会被执行一次
5 */
6 - (void)prepareLayout{
7 [super prepareLayout];
8
9 [self initData];
10
11 [self initCellWidth];
12
13 [self initCellHeight];
14
15 }
复制代码
1 /**
2 * 该方法返回CollectionView的ContentSize的大小
3 */
4 - (CGSize)collectionViewContentSize{
5
6 CGFloat height = [self maxCellYArrayWithArray:_cellYArray];
7
8 return CGSizeMake(SCREEN_WIDTH, height);
9 }
复制代码
1 /**
2 * 该方法为每一个Cell绑定一个Layout属性~
3 */
4 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
5 {
6
7 [self initCellYArray];
8
9 NSMutableArray *array = [NSMutableArray array];
10
11 //add cells
12 for (int i=0; i < _numberOfCellsInSections; i++)
13 {
14 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
15
16 UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
17
18 [array addObject:attributes];
19 }
20
21 return array;
22
23 }
复制代码
(1)Cell宽度计算:若是瀑布流的列数和Cell的Padding肯定了,那么每一个Cell的宽度再经过屏幕的宽度就能够计算出来了。post
(2)Cell高度计算:经过随机数生成的高度优化
(3)Cell的X轴坐标计算:经过列数,和Padding,以及每一个Cell的宽度很容易就能够计算出每一个Cell的X坐标。
(4)Cell的Y轴坐标计算:经过Cell所在列的上一个Cell的Y轴坐标,Padding, 和 上一个Cell的高度就能够计算下一个Cell的Y坐标,并记录在Y坐标的数组中了。
/**
* 该方法为每一个Cell绑定一个Layout属性~
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame = CGRectZero;
CGFloat cellHeight = [_cellHeightArray[indexPath.row] floatValue];
NSInteger minYIndex = [self minCellYArrayWithArray:_cellYArray];
CGFloat tempX = [_cellXArray[minYIndex] floatValue];
CGFloat tempY = [_cellYArray[minYIndex] floatValue];
frame = CGRectMake(tempX, tempY, _cellWidth, cellHeight);
//更新相应的Y坐标
_cellYArray[minYIndex] = @(tempY + cellHeight + _padding);
//计算每一个Cell的位置
attributes.frame = frame;
return attributes;
}
复制代码
/**
* 初始化相关数据
*/
- (void) initData{
_numberOfSections = [self.collectionView numberOfSections];
_numberOfCellsInSections = [self.collectionView numberOfItemsInSection:0];
//经过回调获取列数
_columnCount = 5;
_padding = 5;
_cellMinHeight = 50;
_cellMaxHeight = 150;
}
复制代码
1 /**
2 * 根据Cell的列数求出Cell的宽度
3 */
4 - (void) initCellWidth{
5 //计算每一个Cell的宽度
6 _cellWidth = (SCREEN_WIDTH - (_columnCount -1) * _padding) / _columnCount;
7
8 //为每一个Cell计算X坐标
9 _cellXArray = [[NSMutableArray alloc] initWithCapacity:_columnCount];
10 for (int i = 0; i < _columnCount; i ++) {
11
12 CGFloat tempX = i * (_cellWidth + _padding);
13
14 [_cellXArray addObject:@(tempX)];
15 }
16
17 }
复制代码
1 /**
2 * 随机生成Cell的高度
3 */
4 - (void) initCellHeight{
5 //随机生成Cell的高度
6 _cellHeightArray = [[NSMutableArray alloc] initWithCapacity:_numberOfCellsInSections];
7 for (int i = 0; i < _numberOfCellsInSections; i ++) {
8
9 CGFloat cellHeight = arc4random() % (_cellMaxHeight - _cellMinHeight) + _cellMinHeight;
10
11 [_cellHeightArray addObject:@(cellHeight)];
12 }
13
14 }
复制代码
/**
* 初始化每列Cell的Y轴坐标
*/
- (void) initCellYArray{
_cellYArray = [[NSMutableArray alloc] initWithCapacity:_columnCount];
for (int i = 0; i < _columnCount; i ++) {
[_cellYArray addObject:@(0)];
}
}
复制代码
1 /**
2 * 求CellY数组中的最大值并返回
3 */
4 - (CGFloat) maxCellYArrayWithArray: (NSMutableArray *) array{
5 if (array.count == 0) {
6 return 0.0f;
7 }
8
9 CGFloat max = [array[0] floatValue];
10 for (NSNumber *number in array) {
11
12 CGFloat temp = [number floatValue];
13
14 if (max < temp) {
15 max = temp;
16 }
17 }
18
19 return max;
20 }
复制代码
1 /**
2 * 求CellY数组中的最小值的索引
3 */
4 - (CGFloat) minCellYArrayWithArray: (NSMutableArray *) array{
5
6 if (array.count == 0) {
7 return 0.0f;
8 }
9
10 NSInteger minIndex = 0;
11 CGFloat min = [array[0] floatValue];
12
13 for (int i = 0; i < array.count; i ++) {
14 CGFloat temp = [array[i] floatValue];
15
16 if (min > temp) {
17 min = temp;
18 minIndex = i;
19 }
20 }
21
22 return minIndex;
23 }
复制代码
自定义集合视图控制器布局第一阶段就先到这,下篇博客会在此基础上进一步开发。把上述写死的配置参数,经过Delegate提供,使其在UICollectionView可进行配置,其配置方式相似于UICollectionViewDelegateFlowLayout的代理方法。
上述代码gitHub分享地址:github.com/lizelu/Cust…
你认为如何?请经过加咱们的交流群 点击此处进交流群 ,来一块儿交流或者发布您的问题,意见或反馈。
做者:青玉伏案 出处:www.cnblogs.com/ludashi/