iOS 瀑布流布局實現(xiàn)詳解

新建文件繼承自UICollectionViewLayout
.h內(nèi)容如下:

@class WaterFlowLayout;
@protocol WaterFlowLayoutDelegate <NSObject>

//使用delegate取得每一個Cell的高度
- (CGFloat)waterFlow:(WaterFlowLayout *)layout heightForCellAtIndexPath:(NSIndexPath *)indexPath;

@end

@interface WaterFlowLayout : UICollectionViewLayout

//聲明協(xié)議
@property (nonatomic, weak) id <WaterFlowLayoutDelegate> delegate;
//確定列數(shù)
@property (nonatomic, assign) NSInteger colum;
//確定內(nèi)邊距
@property (nonatomic, assign) UIEdgeInsets insetSpace;
//確定每個cell之間的距離
@property (nonatomic, assign) NSInteger distance;

@end

.m實現(xiàn)內(nèi)容如下:

@interface WaterFlowLayout ()
//存儲列高的數(shù)組
@property (nonatomic, strong) NSMutableArray *columHeightArr;
//存儲所有cell的尺寸信息
@property (nonatomic, strong) NSMutableArray *cellFrameArr;

@end

@implementation WaterFlowLayout

//colum的set方法
- (void)setColum:(NSInteger)colum{
    if (_colum != colum) {
        _colum = colum;
        //將之前的布局信息失效,重新布局
        [self invalidateLayout];
    }
}

//distance的set方法
- (void)setDistance:(NSInteger)distance{
    if (_distance != distance) {
        _distance = distance;
        [self invalidateLayout];
    }
}

//insetSpace的set方法
- (void)setInsetSpace:(UIEdgeInsets)insetSpace{
    if (!UIEdgeInsetsEqualToEdgeInsets(_insetSpace, insetSpace)) {
        _insetSpace = insetSpace;
        [self invalidateLayout];
    }
}

//自定義layout需要重寫下面的幾個方法
//準(zhǔn)備布局,將item的位置信息計算出來
- (void)prepareLayout{
    //將位置信息和高度信息的數(shù)組實例化
    [self initDataArray];
    //初始化每一列的初始高度
    [self initColumHeightArray];
    //初始化計算出全部cell的高度,并且存入數(shù)組
    [self initAllCellHeight];
}

//將位置信息和高度信息的數(shù)組實例化
- (void)initDataArray{
    //記錄當(dāng)前每一列的高度,所以我們只需要列數(shù)的空間就夠了。
    _columHeightArr = [NSMutableArray arrayWithCapacity:_colum];
    //記錄所有cell的尺寸信息
    _cellFrameArr = [NSMutableArray arrayWithCapacity:0];
}

//初始化每一列的初始高度
- (void)initColumHeightArray{
    for (int i = 0; i < _colum; i++) {
        [_columHeightArr addObject:@(_insetSpace.top)];
    }
}

//初始化計算出全部cell的高度,并且存入數(shù)組
- (void)initAllCellHeight{
    //拿出第一組的全部cell的數(shù)量
    NSInteger allCellNumber = [self.collectionView numberOfItemsInSection:0];
    //取得整個collectionView的寬度
    CGFloat totalWidth = self.collectionView.frame.size.width;
    //取得一行中Cell的總寬度
    CGFloat itemAllWidth = totalWidth - _insetSpace.left - _insetSpace.right - _distance * (_colum - 1);
    //取得每一個cell的寬度
    CGFloat width = itemAllWidth/_colum;
    //循環(huán)計算每一個cell的高度并且將位置信息添加到數(shù)組中
    for (int i = 0; i < allCellNumber; i++) {
        //拿到當(dāng)前的列的信息
        NSInteger currentColum = [self getShortColum];
        //x偏移就是用當(dāng)前的列去乘以寬度和間距,并且加上內(nèi)邊距
        CGFloat xOffset = _insetSpace.left + currentColum * (width + _distance);
        //制造索引路徑
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        //取得y偏移
        CGFloat yOffset = [[_columHeightArr objectAtIndex:currentColum] floatValue] + _distance;
        //取得高度,由實現(xiàn)協(xié)議者提供
        CGFloat height = 0.f;
        if (_delegate && [_delegate respondsToSelector:@selector(waterFlow:heightForCellAtIndexPath:)]) {
            height = [_delegate waterFlow:self heightForCellAtIndexPath:indexPath];
        }
        //整理cell的尺寸信息
        CGRect frame = CGRectMake(xOffset, yOffset, width, height);
        //attributes是用來存儲當(dāng)前indexPath的cell的位置信息的
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        attributes.frame = frame;
        //將位置信息添加到cell尺寸數(shù)組中
        [_cellFrameArr addObject:attributes];
        //改變當(dāng)前列的高度
        _columHeightArr[currentColum] = @(frame.size.height + frame.origin.y);
    }
}

//取得當(dāng)前cell的尺寸
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    return [_cellFrameArr objectAtIndex:indexPath.item];
}

//根據(jù)rect去找出需要布局的cell的位置信息
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    //用來存儲可以展示的cell的位置信息
    NSMutableArray *temp = [NSMutableArray arrayWithCapacity:0];
    for (UICollectionViewLayoutAttributes *attributes in _cellFrameArr) {
        //如果取出的位置信息,在rect的范圍內(nèi),就將這個位置信息,裝入數(shù)組中。
        if (CGRectIntersectsRect(attributes.frame, rect)) {
            [temp addObject:attributes];
        }
    }
    return temp;
}

//指定collection的contentSize
- (CGSize)collectionViewContentSize{
    //內(nèi)容寬度指定為collectionView的寬度(橫向不發(fā)生滾動)
    CGFloat width = self.collectionView.frame.size.width;
    //取出最長的列,將其高度定位長度
    CGFloat height = [self getLongColum];
    return CGSizeMake(width, height);
}

- (CGFloat)getLongColum{
    //記錄當(dāng)前最長的列號
    __block NSInteger currentColum = 0;
    //假設(shè)最長的列高度為0
    __block CGFloat longHeight = 0;
    //枚舉數(shù)組中的元素
    [_columHeightArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj floatValue] > longHeight) {
            longHeight = [obj floatValue];
            currentColum = idx;
        }
    }];
    return longHeight + _insetSpace.bottom;
}

//取得最短的列
- (NSInteger)getShortColum{
    //記錄當(dāng)前最短的列號
    __block NSInteger currentColum = 0;
    //假設(shè)最短的列高度為float的最大值
    __block CGFloat shortHeight = MAXFLOAT;
    //枚舉數(shù)組中的元素
    [_columHeightArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj floatValue] < shortHeight) {
            shortHeight = [obj floatValue];
            currentColum = idx;
        }
    }];
    return currentColum;
}

@end

附:我的博客地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,626評論 1 32
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,146評論 4 61
  • 我有一些朋友喜歡打球,你會覺得他穿上球裝特別好看,因為經(jīng)過運動以后,他的身體跟打球衣服之間的關(guān)系特別地美,里面有一...
    Sunny飛鏡閱讀 134評論 0 0
  • 很多人不愿意寫關(guān)于Android中MVP的文章,為啥呢,費時間,大家更喜歡用視頻的方式,文章寫起來太浪費時間了,所...
    小麥田里的稻草人閱讀 687評論 0 3

友情鏈接更多精彩內(nèi)容