瀑布流

瀑布流原理

1、设置一个数组存放所有的cell的布局属性
2、设置一个数组存放所有列的当前高度、并把新的cell放到高度最短的那列的下面
3、设置代理、由代理来告诉我每个cell的具体高度,根据图片的宽高比计算而来
4、列数、每一列之间的间距、每一行之间的间距、边缘间距等可以设置为代理的方式赋值,若没有完成代理方法也可用默认值

大致步骤:需要重写四个方法

  • - (void)prepareLayout方法:在第一次显示和每次的刷新的时候调用
    0.内容高度为0
    1.清除以前计算的所有高度,然后初始化所有列的高度(边缘间距的top值)
    2.清除以前的所有布局属性,然后创建每一个cell对应的布局属性

- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath方法来创建每一个cell对应的布局属性

2.1、创建布局属性
2.2、设置布局属性的frame,
宽度=collectionView宽度-左右边缘间距-列数减一个列间距然后再除以列数
高度=由代理方法返回,根据图片的宽高比计算来(此代理方法必须实现,否则crash)
x坐标= 左边缘间隙+最短列*(宽度+列间距)
y坐标=最短列

  • 找出高度最短的那一列的思路:

2.2.1、假设第0列为目标最短列
2.2.2、遍历存放所有高度的数组,和第0列作比较,若第0列高度大于其他列,则交换高度并设为目标列,即找到最短列
2.3、更新最短那列的高度
2.4、记录内容的高度

  • - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect方法返回之前保存的所有cell的布局属性数组

  • - (CGSize)collectionViewContentSize设置collectionViewcontent size以便于滚动

代码如下:

#import <UIKit/UIKit.h>

@class XMGWaterflowLayout;

@protocol XMGWaterflowLayoutDelegate <NSObject>

@required

- (CGFloat)waterflowLayout:(XMGWaterflowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;

@optional

- (CGFloat)columnCountInWaterflowLayout:(XMGWaterflowLayout *)waterflowLayout;

- (CGFloat)columnMarginInWaterflowLayout:(XMGWaterflowLayout *)waterflowLayout;

- (CGFloat)rowMarginInWaterflowLayout:(XMGWaterflowLayout *)waterflowLayout;

- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(XMGWaterflowLayout *)waterflowLayout;

@end
@interface XMGWaterflowLayout : UICollectionViewLayout

/** 代理 */

@property (nonatomic, weak) id<XMGWaterflowLayoutDelegate> delegate;

@end
#import "XMGWaterflowLayout.h"

/** 默认的列数 */

static const NSInteger XMGDefaultColumnCount = 3;

/** 每一列之间的间距 */

static const CGFloat XMGDefaultColumnMargin = 10;

/** 每一行之间的间距 */

static const CGFloat XMGDefaultRowMargin = 10;

/** 边缘间距 */

static const UIEdgeInsets XMGDefaultEdgeInsets = {10, 10, 10, 10};

@interface XMGWaterflowLayout()

/** 存放所有cell的布局属性 */

@property (nonatomic, strong) NSMutableArray *attrsArray;

/** 存放所有列的当前高度 */

@property (nonatomic, strong) NSMutableArray *columnHeights;

/** 内容的高度 */

@property (nonatomic, assign) CGFloat contentHeight;

- (CGFloat)rowMargin;

- (CGFloat)columnMargin;

- (NSInteger)columnCount;

- (UIEdgeInsets)edgeInsets;

@end
@implementation XMGWaterflowLayout

#pragma mark - 常见数据处理

- (CGFloat)rowMargin

{

    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {

        return [self.delegate rowMarginInWaterflowLayout:self];

    } else {

        return XMGDefaultRowMargin;

    }

}

- (CGFloat)columnMargin

{

    if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) {

        return [self.delegate columnMarginInWaterflowLayout:self];

    } else {

        return XMGDefaultColumnMargin;

    }

}

- (NSInteger)columnCount

{

    if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) {

        return [self.delegate columnCountInWaterflowLayout:self];

    } else {

        return XMGDefaultColumnCount;

    }

}

- (UIEdgeInsets)edgeInsets

{

    if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {

        return [self.delegate edgeInsetsInWaterflowLayout:self];

    } else {

        return XMGDefaultEdgeInsets;

    }

}

#pragma mark - 懒加载

- (NSMutableArray *)columnHeights

{

    if (!_columnHeights) {

        _columnHeights = [NSMutableArray array];

    }

    return _columnHeights;

}

- (NSMutableArray *)attrsArray

{

    if (!_attrsArray) {

        _attrsArray = [NSMutableArray array];

    }

    return _attrsArray;

}

/**

 * 初始化

 */

- (void)prepareLayout

{

    [super prepareLayout];

    

    self.contentHeight = 0;

    

    // 清除以前计算的所有高度

    [self.columnHeights removeAllObjects];

    for (NSInteger i = 0; i < self.columnCount; i++) {

        [self.columnHeights addObject:@(self.edgeInsets.top)];

    }

    // 清除之前所有的布局属性

    [self.attrsArray removeAllObjects];

    // 开始创建每一个cell对应的布局属性

    NSInteger count = [self.collectionView numberOfItemsInSection:0];

    for (NSInteger i = 0; i < count; i++) {

        // 创建位置

        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

        // 获取indexPath位置cell对应的布局属性

        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];

        [self.attrsArray addObject:attrs];

    }

}

/**

 * 决定cell的排布

 */

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

{

    return self.attrsArray;

}

/**

 * 返回indexPath位置cell对应的布局属性

 */

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

{

    // 创建布局属性

    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    

    // collectionView的宽度

    CGFloat collectionViewW = self.collectionView.frame.size.width;

    

    // 设置布局属性的frame

    CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;

    CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];

    

    // 找出高度最短的那一列

    NSInteger destColumn = 0;

    CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];

    for (NSInteger i = 1; i < self.columnCount; i++) {

        // 取得第i列的高度

        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

        

        if (minColumnHeight > columnHeight) {

            minColumnHeight = columnHeight;

            destColumn = i;

        }

    }

    

    CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);

    CGFloat y = minColumnHeight;

    if (y != self.edgeInsets.top) {

        y += self.rowMargin;

    }

    attrs.frame = CGRectMake(x, y, w, h);

    

    // 更新最短那列的高度

    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));

    

    // 记录内容的高度

    CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];

    if (self.contentHeight < columnHeight) {

        self.contentHeight = columnHeight;

    }

    return attrs;

}

- (CGSize)collectionViewContentSize

{

//    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];

//    for (NSInteger i = 1; i < self.columnCount; i++) {

//        // 取得第i列的高度

//        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

//        

//        if (maxColumnHeight < columnHeight) {

//            maxColumnHeight = columnHeight;

//        }

//    }

    return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);

}

@end

效果图:

69deb735-dd70-471f-a8ed-d2ee21210cef.png

推荐阅读更多精彩内容

  • github地址little-waterfall-framework-** 这里把整一个瀑布流小框架的实现思路写出...
    Tuberose阅读 5,976评论 12 123
  • 一、瀑布流设计方案 不可取.png 过于复杂.png 最优方案.png 二、瀑布流设计思路分析 1、自定义流水布局...
    CoderSC阅读 232评论 0 0
  • (本文主要讲一下现在比较流行的一种布局方式----瀑布流布局 如有写的不好的地方 还请多多指正 感谢) 1、功能分...
    论丶道阅读 772评论 0 5
  • 实现瀑布流简单,实现分区瀑布流,并且每个区的瀑布流的列数不一样且有区头和区尾,就不是太容易了。我嫌麻烦不愿意自己写...
    ac986bb0e59a阅读 675评论 2 3
  • 三种方法实现 一、scrollView做垫子,上面添加多个tableView(不可取); 效率低下,cell不能循...
    SadMine阅读 402评论 0 0
  • 下午起到晚上,有三场约见,所以午后就晃悠到了屯里吃饭。不知道吃什么,凭着懒惰的直觉去吃隆小宝。这是一家曾经网红了一...
    荒梁阅读 207评论 0 0
  • 倾刻黄昏 我安静地倚靠在门边 等黑夜将我笼罩 我要把一天的情绪 都抛给黑暗 风再起时 有你爱我
    伦小让阅读 111评论 0 2
  • 基础 从webpack文件上来看,主要用到的有entry,output,resolve,module,plugin...
    2林子易2阅读 1,991评论 2 3
  • 暑往寒来,爸爸已经在联通公司工作了二十多年。从我记事以来,我总觉得爸爸的工作相当轻松,并没有那么劳累...
    蒹葭essay阅读 106评论 0 0