1. 如果系统自带的布局的话,是这样:

    //系统自带的UICollectionViewFlowLayout 而不是UICollectionViewLayout
UICollectionViewFlowLayout *waterLayout = [[UICollectionViewFlowLayout alloc]init];
waterLayout.itemSize = CGSizeMake(, );
waterLayout.minimumLineSpacing = ;
waterLayout.minimumInteritemSpacing = ;
waterLayout.scrollDirection = UICollectionViewScrollDirectionVertical;

而自定义的话:WaterFlowLayout : UICollectionViewLayout

系统UICollectionViewFlowLayout也是继承自UICollectionViewLayout

2.  主要实现部分:
在- (void)prepareLayout;方法中计算好所有item的布局属性,主要是frame属性,而frame属性包括(x,y, w,h)

因为系统的这个方法

//滚动时是否重新计算
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
}

所以- (void)prepareLayout;计算前都要做一次清空:

/**
* 每次布局之前的准备 最先调用
*/
- (void)prepareLayout{ NSLog(@"prepareLayout");//调一次
// 1.清空最大的Y值 清空是为了防止滚动个计算出问题
for (int i = ; i<self.columnsCount; i++) {
NSString *column = [NSString stringWithFormat:@"%d", i];
//NSLog(@"self.sectionInset.top: %f",self.sectionInset.top);//
self.maxYDict[column] = @(self.sectionInset.top);//sectionInset有上下左右
} // 2.计算所有cell的属性
[self.attrsArray removeAllObjects];
//获得有多少个item
NSInteger count = [self.collectionView numberOfItemsInSection:];
for (int i = ; i< count; i++) {
//这个获取属性方法下面有重写
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:]];
[self.attrsArray addObject:attrs];
}
}

上面代码中这部分,为每个item设置属性,然后扔到一个属性数组,所以重点在这里

UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:]];

重写他:

//重写 计算item属性方法 2 思路从需要的值往前推 这个方法最重要
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
// NSLog(@"self.maxYDict %@",self.maxYDict);
// {
// 0 = 30;
// 1 = 30;
// 2 = 30;
// } //假设一个最小值
__block NSString *minColumn = @"";
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString * column, id _Nonnull obj, BOOL * _Nonnull stop) {
//找到每一行最短的那个列 0/1/2 后面往这个列加下一张图片的height
if ([obj floatValue] < [self.maxYDict[minColumn] floatValue]) {
minColumn = column;
}
}]; //计算尺寸
CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - (self.columnsCount -)*self.columnMargin)/self.columnsCount;
//CGFloat height = 100 + arc4random_uniform(100);//先随机给个高度 这样做滚动时会出现烟花缭乱的
CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];//让代理去帮忙计算 //NSLog(@"height %f",height);
//计算位置
CGFloat x = self.sectionInset.left + (width + self.columnMargin) *[minColumn intValue];
CGFloat y = [self.maxYDict[minColumn] floatValue] + self.rowMargin;
self.maxYDict[minColumn] = @(y + height); // 创建属性
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.frame = CGRectMake(x, y, width, height);
return attrs;
}

系统有一个方法会返回所有属性,而返回值是一个数组,数组里装的就是所有item的布局属性,所以重写这个方法:

//重写返回所有item属性方法
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSLog(@"layoutAttributesForElementsInRect");
//在prepare一次性算好 然后扔过来
return self.attrsArray;
}

最后整块UICollectionView的content又一个大小需要给出来,重写下面这个方法即可,也就是最大的y+height :

//重写 返回collectionView的 Size方法 这个要计算完所有item才知道
//这个方法子类和父类都会调一次 所以会调2次 不奇怪
-(CGSize)collectionViewContentSize{
//[super prepareLayout]; __block NSString *maxCloumn = @"";
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj floatValue] > [self.maxYDict[maxCloumn] floatValue]) {
maxCloumn = key;
}
}];
NSLog(@"collectionViewContentSize - %f",[self.maxYDict[maxCloumn] floatValue]);//一次全部item布局调2次
return CGSizeMake(, [self.maxYDict[maxCloumn] floatValue]);//0表示不支持水平滚动,最大y值可以遍历 }

另外:因为每个item的高度是要计算的,可以写个私有方法,也可以让ViewController来成为代理,让他帮忙计算。这里用代理实现

//CGFloat height = 100 + arc4random_uniform(100);//先随机给个高度 这样做滚动时会出现烟花缭乱的
CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];//让代理去帮忙计算

利用代理的好处,MJ老师说了:

利用控制器成为自定义布局类的代理,返回想要的高度。利用代理的好处,就是在布局想拿到控制器一些东西的时候,防止布局系统需要的是一个模型时,如果你利用布局一个属性来传值,那么布局系统永远跟模型相关了。为了防止这种依赖关系,所以用代理来做,比较合适,框架类的东西不容易产生对外部代码的依赖。通用型较好。

Demo地址:https://github.com/nwgdegitHub/Waterfalls-flow

另外这边文章实现原理大致相同,值得借鉴。

https://www.jianshu.com/p/995c0f35e7fb

MJ瀑布流学习笔记的更多相关文章

  1. IO流学习笔记(二)之BufferedWriter与BufferedReader及实例Demo

    在之前的学习笔记(http://blog.csdn.net/megustas_jjc/article/details/72853059)中,FileWriter与FileReader的Demo使用的中 ...

  2. JAVA.IO流学习笔记

    一.java.io 的描述 通过数据流.序列化和文件系统提供系统输入和输出.IO流用来处理设备之间的数据传输 二.流 流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数 ...

  3. JAVA输入/输出系统中的其他流学习笔记

    一.字节数组流 字节数组流类能够操作内存中的字节数组,它的数据是一个字节数组.字节数组流类本身适配器设计模式,它把字节数组类型转为流类型使得程序能够对字节数组进行读写操作. 1.ByteArrayIn ...

  4. zkw费用流 学习笔记

    分析 记\(D_i\)为从\(S\)出发到\(i\)的最短路 最短路算法保证, 算法结束时 对于任意存在弧\((i,j)\)满足\(D_i + c_{ij}\ge D_j\) ① 且对于每个 \(j\ ...

  5. IO流学习笔记

    1.File类 文件和目录路径名的抽象表示形式. 4种构造方法 File(File parent, String child) File(File parent, String child) File ...

  6. 最小费用最大流 学习笔记&&Luogu P3381 【模板】最小费用最大流

    题目描述 给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 题目链接 思路 最大流是没有问题的,关键是同时保证最小费用,因此,就可以把 ...

  7. IO流学习笔记(一)之FileWriter与FileReader

    IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的对象都在IO包中 流按照操作数据分为两种:字节流和字符流 流按流向分为:输入流和输出流 输入流和输出流是相对于 ...

  8. CSS3与页面布局学习笔记(四)——页面布局大全(负边距、双飞翼、多栏、弹性、流式、瀑布流、响应式布局)

    一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...

  9. web前端学习笔记-瀑布流的算法分析与代码实现

    瀑布流效果目前应用很广泛,像花瓣,新浪轻博,蘑菇街,美丽说等好多网站都有.也有好多支持该效果的前段框架,今天学习了一下这种效果的实现,不依赖插件,自己动手分析实现过程,为了便于叙述清楚,分析中的一些名 ...

随机推荐

  1. 【转】最全的 pip 使用指南,50% 你可能没用过

    [转]最全的 pip 使用指南,50% 你可能没用过 所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 P ...

  2. sc 使用了配置中心后,如何设置远程和本地配置的优先级

    在 spring 中,如何获取一个 key 的值? applicationContext.getEnvironment().getProperty("swagger.show") ...

  3. 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第8节 Math类_18_数学工具类Math

    常用几个数学的方法 abs绝对值 ceil向上取整,它并不是四舍五入 floor向下取整 round四舍五入 PI 按住Ctrl+鼠标左键 进入Math这个类的源码里面 Ctrl+F12 然后输入PI ...

  4. Delphi 跨单元进入(访问)类的私有成员,protected ,private部分

    http://blog.sina.com.cn/s/blog_5f8861b60102v1nl.html Delphi 跨单元进入(访问)类的私有成员,protected ,private部分 (20 ...

  5. G2 基本使用 折线图 柱状图 饼图 基本配置

    G2的基本使用 1.浏览器引入  <!-- 引入在线资源 --> <script src="https://gw.alipayobjects.com/os/lib/antv ...

  6. 【ABAP系列】SAP ABAP 优化LOOP循环的一点点建议

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 优化LOOP循 ...

  7. 【ABAP系列】SAP 业务界面同时显示KEY和文本

      公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP 业务界面同时显示KEY和 ...

  8. 剑指offer--day10

    1.1 题目:二叉搜索树的后序遍历序列:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 1.2 思路: 以{ ...

  9. dataTables使用的详细说明整理

    本文共三个部分:官网|基本使用|遇到的问题 一.官方网站:http://www.datatables.club/ 二.基本使用: 1.dataTables的引入及初始化 <!--第一步:引入Ja ...

  10. react 样式的写法之一 ---》styled-components的基本使用

    [react]---styled-components的基本使用---[WangQi]   一.官网地址 https://www.styled-components.com/ 二.styled-com ...