纵向等宽多组瀑布流

2020-05-11  本文已影响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;
}

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

上一篇下一篇

猜你喜欢

热点阅读