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. 20160711--C# 委托的三种调用示例(同步调用 异步调用 异步回调)【转载】

    首先,通过代码定义一个委托和下面三个示例将要调用的方法: 代码如下: public delegate int AddHandler(int a,int b); public class 加法类 { p ...

  2. pycharm运行时出现‘no module named 'requests'’

    参考文章:https://www.jianshu.com/p/f7c808365a9e 出现无requests的包的错误--->找包中是否有requests 1.从files下拉框中点击sett ...

  3. mysql恢复备份导出

    MySQL-5.7 备份与恢复   一.备份分类 按介质分类: 物理备份指通过拷贝数据库文件方式完成备份,适用于数据库很大,数据重要且需要快速恢复的数据库. 逻辑备份指通过备份数据库的逻辑结构和数据内 ...

  4. VS2012发布Web应用程序

    一.右键项目-->发布 二.配置文件:新建-->配置文件名称,如MyProject 三.连接:1.发布方法:文件系统  2.目标位置:自己在本地建立一个文件夹 3.目标URL:可以不填 四 ...

  5. spring源码下载及转入ECLIPSE

    转自:https://www.cnblogs.com/scevecn/p/6043284.html 本例spring源码版本是4.3.0的, 所以jdk需要准备1.8的(不同版本源码要求的jdk不一样 ...

  6. Dev Express之ImageComboBoxEdit,RepositoryItemImageComboBox使用方式

     Dev Express之ImageComboBoxEdit,RepositoryItemImageComboBox使用方式 1.使用ImageComboBoxEdit实现下拉框键值数据函数 publ ...

  7. Kubeadm安装Kubernetes 1.15.1

    一.实验环境准备 服务器虚拟机准备 IP CPU 内存 hostname 192.168.198.200 >=2c >=2G master 192.168.198.201 >=2c ...

  8. HDU 6468 /// DFS

    题目大意: 把 1~15 的数字典序排序后为 1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9 此时给定 n k, 求1~n的数组字典序排序后 第k个 ...

  9. 使用OFBIZ的理由和不使用OFBIZ的理由

    1 使用OFBIZ的理由 1.1 什么是OFBIZ OFBIZ是由Sourceforge维护的一个最著名的开源项目之一,提供创建基于最新J2EE/XML规范和技术标准,构建大型企业级.跨平台.跨数据库 ...

  10. Bootstrap真的总是好的吗

    原文地址:Bootstrap considered harmful 原文作者:Hidde de Vries 译文出自:neal 译者: Neal 个人主页:http://neal1991.python ...