最近开发中遇到了关于瀑布流布局的需求,所有就整理了一个瀑布流布局类,使用时只需要调整列数、行间距、列间距、上下左右边缘就可以做出各种需求的瀑布流布局,下面直接上代码:

自定义瀑布流需要继承UICollectionViewLayout布局类

.h文件


#import <UIKit/UIKit.h>

@interface ZYYWaterLayout :UICollectionViewLayout

@end
 

.m文件

#import "ZYYWaterLayout.h"
//默认的列数
staticconst CGFloat ZYYCollumCoutnt =3;
//列间距
staticconst CGFloat ZYYCollumMargin =10;
//行间距
staticconst CGFloat ZYYRowMargin =10;
//边缘间距
staticconst UIEdgeInsets ZYYEdgeUnsets = {10,10,10,10};

@interfaceZYYWaterLayout()

//用于存放cell属性的数组
@property (strong,nonatomic)NSMutableArray *attrsArray;
//用于存放所有列高度数组
@property (strong,nonatomic)NSMutableArray *maxH;

@end

@implementation ZYYWaterLayout

//懒加载属性数组
- (NSMutableArray *)attrsArray{
    if (_attrsArray ==nil) {
        _attrsArray = [NSMutableArrayarray];
        
    }
    return_attrsArray;
}

//懒加载高度属性数组
- (NSMutableArray *)maxH{
    if (_maxH ==nil) {
        _maxH = [NSMutableArrayarray];
        
    }
    return_maxH;
}


//准备布局每次刷新都会调用此方法
- (void)prepareLayout{
    [superprepareLayout];
    
   //先清除以前计算的所有高度,这种做法比较严谨,如果有下拉刷新,不及时清空数组的话会造成数据混乱
    [self.maxHremoveAllObjects];
    
   //这里先将数组里面的初始值设为0
    for (NSInteger i =0; i < ZYYCollumCoutnt; i ++) {
        [self.maxHaddObject:@0];
    }

     NSLog(@"%s",__func__);
   //清除以前的所有属性
    [self.attrsArrayremoveAllObjects];

   //开始创建每个cell对应的布局属性
    //1、获取collectionView里面的有多少个item
    NSInteger count = [self.collectionViewnumberOfItemsInSection:0];
    
    //创建多少collectionViewcell属性
    for (NSInteger i=1; i<count; i++) {
        //获取item对应的indexPath
        NSIndexPath *indexPath = [NSIndexPathindexPathForItem:i inSection:0];
        
        //创建属性调用下面的layoutAttributesForItemAtIndexPath方法
        UICollectionViewLayoutAttributes *attrs = [selflayoutAttributesForItemAtIndexPath:indexPath];
        //设置cell的属性直接调用layoutAttributesForItemAtIndexPath方法即可
        
        [self.attrsArrayaddObject:attrs];
    }
    
}

/**
 * 决定布局的关键所在
 *
 *  @param rect 属性rect
 *
 *  @return 返回属性数组
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    
    NSLog(@"%s",__func__);

    returnself.attrsArray;
}

/**
 *  返回indexPath位置cell对应的布局属性
 * 这个方法是核心算法
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    //创建属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];
    
    //collectionView的宽度
    CGFloat collectionViewW =self.collectionView.frame.size.width;
    
    //列号
    NSInteger cloumIndex =0;
   //默认第一行是最小的,这样做的话可以让下面的for循环从i=1开始遍历,这样做可以优化性能
    CGFloat minClumnHeight = [self.maxH[0]doubleValue];
    
    for (NSInteger i =1; i < ZYYCollumCoutnt; i++) {
        //取出第i列元素的y值
        CGFloat cheight = [self.maxH[i]doubleValue];
        if (minClumnHeight > cheight) {
            minClumnHeight = cheight;
            cloumIndex = i;
        }
    }
    
    //每个item的宽度 == (collectionView的宽度 -左边距 -右边距 -(列数-1)*间距)再除于列数
    CGFloat w = (collectionViewW -ZYYEdgeUnsets.left -ZYYEdgeUnsets.right - (ZYYCollumCoutnt -1) * ZYYCollumMargin)/3.0;
    
   //高度这里用的是随机数,做项目时根据的是素材的高度
    CGFloat h =50 + arc4random_uniform(100);
    
    //x可以根据列来算
    CGFloat x =ZYYEdgeUnsets.left + cloumIndex * (ZYYRowMargin + w);

    //y最小itme值计算
    CGFloat y = minClumnHeight +ZYYRowMargin;
 
    //设置布局属性的frame,这个frame是最终item的frame
     attrs.frame =CGRectMake(x, y, w, h);

   //更新最短那列的高度
    self.maxH[cloumIndex] =@(CGRectGetMaxY(attrs.frame));
    
    return attrs;
}

//尺寸
- (CGSize)collectionViewContentSize{
    
   //找出y值最大的的那一列,和上面找出最小高度相似
    CGFloat maxColunmHeight = [self.maxH[0]doubleValue];
    for (NSInteger i =1; i < ZYYCollumCoutnt; i++) {
        CGFloat my = [self.maxH[i]doubleValue];
        if (maxColunmHeight < my) {
            maxColunmHeight = my;
        }
    }
    
    returnCGSizeMake(0, maxColunmHeight +ZYYEdgeUnsets.bottom);
}

结语:上面的注释比较清楚,用的时候直接将代码复制粘贴就可以使用了


@end