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. Stream 源码分析

    Stream 支持顺序和并行聚合操作的一组元素序列. 1)operations:支持在单个元素上执行的操作,流操作分为中间操作和终止操作 1-1)中间操作: 1-1-1)无状态:unordered() ...

  2. ThreadPoolExecutor 源码分析

    ThreadPoolExecutor 线程池核心实现类 线程池的生命周期 RUNNING: 接受新任务,同时处理工作队列中的任务 SHUTDOWN: 不接受新任务,但是能处理工作队列中的任务 STOP ...

  3. EDM营销应注意要定期发送邮件

    一个成熟的EDM营销方案应该要确定完整的邮件发送频率,并且严格按照计划执行.这点在EDM营销过程中非常重要,下面为大家分析一下. 一个EDM营销应该确定一下发送的时间,每月或者每周发送一次,这样用户能 ...

  4. leetcode 39. 组合总和(python)

    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制重复被选 ...

  5. 鸡肋工具-Oracle建表工具

    为什么叫鸡肋工具呢,因为我们完全可以在pl/sql上直接建立表.索引.同义词.授权.触发器等. 写这个工具目的是因为公司的本地.测试环境开发无权创建表,每次成员建表语句千奇百怪不规范,所以写了这么个工 ...

  6. 测开之路一百二十三:快速搭建python虚拟环境

    前提:已装好python3.4+且环境可正常运行 一:手动搭建: 准备好一个工作目录 管理员运行cmd,进入到准备的目录里面 执行命令:python -m venv 虚拟环境名 激活虚拟环境(在ven ...

  7. 多进程---multiprocessing/threading/

    一.多进程:multiprocessing模块 多用于处理CPU密集型任务 多线程 多用于IO密集型任务 Input Ouput 举例: import multiprocessing,threadin ...

  8. The bean 'dataSource', defined in BeanDefinition defined in class path resou

    SpringCloud对应SpringBoot不匹配 Greenwich 2.1.x Finchley 2.0.x Edgware 1.5.x Dalston 1.5.x SpringBoot Spr ...

  9. 关于 阿里云 的linux 安装 jdk和tomcat 中的问题(解压版的jdk和tomcat)

    一安装jdk 1:首先要申请一个服务器 2:其次官网下载jdk(按你的linux版本下载(我的目前是64)) 3:就是上传jdk 4:在 usr 文件夹下创建 java 文件夹 5:把jdk上传到ja ...

  10. WPF资源字典的使用

    1.在解决方案中添加资源字典:鼠标右键——添加——资源字典 2.在资源字典中写入你需要的样式,我这里简单的写了一个窗体的边框样式 3.在App.xaml中加入刚刚新建的资源字典就好了