美文网首页
纵向等宽多组瀑布流

纵向等宽多组瀑布流

作者: 雷霆丶风暴烈酒 | 来源:发表于2020-05-11 16:09 被阅读0次

    瀑布流

    TTWaterfallFlowLayout.h
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @class TTWaterfallFlowLayout;
    
    @protocol TTWaterfallFlowLayoutDelegate <NSObject>
    @required
    - (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
    @optional
    - (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath;
    - (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForFooterAtIndexPath:(NSIndexPath *)indexPath;
    - (NSInteger)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout numForColumnsInSection:(NSInteger )section;
    @end
    
    @interface TTWaterfallFlowLayout : UICollectionViewFlowLayout
    
    /// 左边距 默认10
    @property (nonatomic, assign) CGFloat leftMargin;
    
    /// 右边距  默认10
    @property (nonatomic, assign) CGFloat rightMargin;
    
    /// 行间距
    @property (nonatomic, assign) CGFloat rowMargin;
    
    /// 列边距
    @property (nonatomic, assign) CGFloat columnsMargin;
    
    /// 列数  默认2列 == 
    @property (nonatomic, assign) NSInteger columns;
    
    @property (nonatomic, weak) id<TTWaterfallFlowLayoutDelegate>delegate;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    TTWaterfallFlowLayout.m
    #import "TTWaterfallFlowLayout.h"
    
    @interface TTWaterfallFlowLayout ()
    
    @property (nonatomic, strong) NSMutableArray *attributeArray;
    @property (nonatomic, strong) NSMutableArray *lowestYArr;
    @end
    
    @implementation TTWaterfallFlowLayout
    
    - (instancetype)init {
        self = [super init];
        if (self) {
            self.lowestYArr = [NSMutableArray arrayWithArray:@[@(0),@(0)]];
            
            self.leftMargin =10;
            self.rightMargin =10;
            self.columnsMargin =10;
            self.rowMargin =10;
            self.columns = 2;
        }
        return self;
    }
    
    - (void)prepareLayout {
        //清除历史布局
        self.attributeArray = [NSMutableArray array];
        [self.lowestYArr removeAllObjects];
        for (int i=0; i<self.columns; i++) {
            [self.lowestYArr addObject:@(0)];
        }
        
        [super prepareLayout];
        
        //开始创建每一组cell的布局属性
        NSInteger sectionCount =  [self.collectionView numberOfSections];
        for(NSInteger section = 0; section < sectionCount; section++) {
               
            //头视图
            if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForHeaderAtIndexPath:)]) {
                UICollectionViewLayoutAttributes *headerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
                [self.attributeArray addObject:headerAttrs];
            }
                   
            //cell
            NSInteger rowCount = [self.collectionView numberOfItemsInSection:section];
            for (NSInteger row = 0; row < rowCount; row++) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:section];
                UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
                [self.attributeArray addObject:attrs];
            }
            
            //尾视图
            if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForFooterAtIndexPath:)]) {
                UICollectionViewLayoutAttributes *footerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
                [self.attributeArray addObject:footerAttrs];
            }
        }
    }
    //这个方法中返回我们的布局数组
    -(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
        return self.attributeArray;
    }
    //返回cell的布局
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
        //获取列数,根据列数改变
        if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:numForColumnsInSection:)]) {
            self.columns = [self.delegate waterfallFlowLayout:self numForColumnsInSection:indexPath.section];
        }
        
        UICollectionViewLayoutAttributes *attri = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        //计算
        attri.frame = [self itemFrameOfVerticalWaterfallFlow:indexPath];
        return attri;
    }
    //返回头尾视图的布局
    - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
        UICollectionViewLayoutAttributes *attri;
        
        if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) { //头视图
            attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
            CGFloat h = 0;
            if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForHeaderAtIndexPath:)]) {
                h = [self.delegate waterfallFlowLayout:self sizeForHeaderAtIndexPath:indexPath].height;
            }
            attri.frame = CGRectMake(0, [self getHighestYInArr:self.lowestYArr], SCREEN_WIDTH, h);
        }else { //脚视图
            attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];
            CGFloat h = 0;
            if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForFooterAtIndexPath:)]) {
                h = [self.delegate waterfallFlowLayout:self sizeForHeaderAtIndexPath:indexPath].height;
            }
            attri.frame = CGRectMake(0, [self getHighestYInArr:self.lowestYArr], SCREEN_WIDTH, h);
        }
        // 更新Y数据
        CGFloat highest = [self getHighestYInArr:self.lowestYArr] +attri.bounds.size.height;
        NSInteger count = self.lowestYArr.count;
        [self.lowestYArr removeAllObjects];
        for (int i=0; i<count; i++) {
            [self.lowestYArr addObject:@(highest)];
        }
        
        return attri;
    }
    - (CGSize)collectionViewContentSize {
        return CGSizeMake(SCREEN_WIDTH, [self getHighestYInArr:self.lowestYArr]+20);
    }
    
    //竖向瀑布流 item等宽不等高
    - (CGRect)itemFrameOfVerticalWaterfallFlow:(NSIndexPath *)indexPath{
        //设置布局属性item的frame
        //宽度根据左边距、右边距、列边距、列数自动计算
        CGFloat h = [self.delegate waterfallFlowLayout:self sizeForItemAtIndexPath:indexPath].height;
        CGFloat w = (SCREEN_WIDTH-self.leftMargin-self.rightMargin-(self.columns-1)*self.columnsMargin)/self.columns;
        
        NSInteger index = [self getLowestYIndexInArr:self.lowestYArr];
        CGFloat x = self.leftMargin+(w+self.columnsMargin)*index;
        CGFloat y = [self.lowestYArr[[self getLowestYIndexInArr:self.lowestYArr]] floatValue];
        
        [self.lowestYArr replaceObjectAtIndex:index withObject:@(y+h+self.rowMargin)];
        
        return CGRectMake(x, y, w, h);
    }
    
    #pragma mark -- GET
    // 获取相对最高的index
    - (NSInteger)getLowestYIndexInArr:(NSMutableArray <NSNumber *>*)arr {
        
        NSInteger index = 0;
        CGFloat min = [arr[0] floatValue];
        
        for (int i=0; i<arr.count; i++) {
            if ([arr[i] floatValue] < min) {
                min = [arr[i] floatValue];
                index = i;
            }
        }
        
        return index;
    }
    // 获取最低的位置
    - (CGFloat)getHighestYInArr:(NSMutableArray <NSNumber *>*)arr {
        
        CGFloat max = [arr[0] floatValue];
        
        for (int i=0; i<arr.count; i++) {
            if ([arr[i] floatValue] > max) {
                max = [arr[i] floatValue];
            }
        }
        
        return max;
    }
    #pragma mark -- SET
    - (void)setColumns:(NSInteger)columns {
        if (_columns != columns) { //列数更改后需要重新设置y数据
            //需重新设置位置
            CGFloat highest = [self getHighestYInArr:self.lowestYArr];
            [self.lowestYArr removeAllObjects];
            for (int i=0; i<columns; i++) {
                [self.lowestYArr addObject:@(highest)];
            }
        }
        _columns = columns;
    }
    @end
    

    使用

    TTWaterfallFlowLayout *flowLayout = [TTWaterfallFlowLayout new];
    flowLayout.delegate = self;
    

    同系统创建flowLayout,设置代理

    实现代理方法

    #pragma mark -- TTWaterfallFlowLayoutDelegate
    - (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath {
        // 代码里写死为屏幕宽度,这里宽度无用到
        return CGSizeMake(0,150);
    }
    - (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
        // 因为只实现了等宽不等高的一种情况。所以宽度是自动根据间距自动算出来的。这里宽度无效,可自行扩展
        if (indexPath.section == 0) {
            return CGSizeMake(0, 60);
        }else if (indexPath.section == 1) {
            return CGSizeMake(0, 160);
        }
        return CGSizeMake(0, 200+arc4random()%200);
    }
    - (NSInteger)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout numForColumnsInSection:(NSInteger)section {
        if (section == 0) {
            return 5;
        }else if (section == 1) {
            return 3;
        }
        return 2;
    }
    

    代码里用到了部分自定义宏,自行更改即可

    相关文章

      网友评论

          本文标题:纵向等宽多组瀑布流

          本文链接:https://www.haomeiwen.com/subject/hlsbwhtx.html