算法总体思路

先说一下总体上的思路。既然图片的大小、位置各不一样,我们很自然地会想到需要算出每个item的frame,然后把这些frame赋值给当前item的UICollectionViewLayoutAttributes。

自定义UICollectionViewLayout的关键两步是先后重载下面两个方法:

- (void)prepareLayout;

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;

所以我们的思路是在- (void)prepareLayout;方法中算出所有item的frame,并赋值给当前item的UICollectionViewLayoutAttributes。用图片的形式比较直观:

接下来问题就化归到了如何求每个item的frame。

这里我们抽象出一个列的概念:

除此之外,我们还需要维护一个存储高度的数组COLUMNSHEIGHTS。数组中有n个元素,n表示所有列数,如上图,n = 3。缓存的值表示当前列的高度,上图的例子中,COLUMNSHEIGHTS = [104,123,89]。

然后我们把item逐个放入列中,以这样的规则:从左到右,item优先放入COLUMNSHEIGHTS中最短的列。

打个比方,下一个item就应该放入最短的列,也就是第三列:

以此规则,循环下去,直到所有item都放置完毕。

细节

item.frame.origin:

用自然语言描述,

坐标x应该是这样的:(最短列的编号-1)x 列宽

坐标y应该是这样的:从COLUMNSHEIGHTS中取出最短列对应的高度

所以我们需要一个算法来找出当前COLUMNSHEIGHTS中的最短的列,最直接的方法就是0(n)时间复杂度的循环比较,这里还好因为数据量比较少,如果遇到数据量大的情况可能就需要考虑分治法了。

//寻找此时高度最短的列.第一列为0

-(NSUInteger)findShortestColumn{

NSUInteger shortestIndex = 0;

CGFloat shortestValue = MAXFLOAT;

NSUInteger index=0;//游标

for (NSNumber *columnHeight in self.COLUMNSHEIGHTS) {

if ([columnHeight floatValue] < shortestValue) {

shortestValue = [columnHeight floatValue];

shortestIndex = index;

}

index++;

}

return shortestIndex;

}

找到了最短列,表达出item的x坐标和y坐标就很容易了:

NSUInteger origin_x = [self findShortestColumn] * [self columnWidth];

NSUInteger origin_y = [self.COLUMNSHEIGHTS[shtIndex] integerValue] ;

item.frame.size.width:

由于列数是有用户决定的,所以是个变量,由此可以获得列宽columnWidth = self.collectionView.bounds.size.width /self.columnsCount

然后我们规定,默认情况下item的宽度等于columnWidth。当满足当前列和下一列(如上图红色方块,就是属于当前列位于列2,下一列列3)高度相等时,可以横跨两栏。(再看红色方块,因为在它放进去之前,第二列高度为0,第三列高度也为0,满足横跨的条件)

但是!

如果出现了下面的这种情况:

也就是说,单单满足当前列和下一列高度相等还不够,因为只要一旦满足这个条件,接下去将会一直是横跨的状态。所以我们还需要再加一个条件来筛选这些即使满足当前列和下一列高度相等的item。我们可以用随机数:

NSUInteger randomOfWhetherDouble = arc4random() % 100;//随机数标记是否要双行

arc4random() % 100;会随机生成一个0~100的整数。然后我们设定一个阈值,比如40.只有当同时满足当前列和下一列高度相等和 randomOfWhetherDouble < 40 的item才能真正实现跨行。换句话说,即使当前列和下一列高度相等,也只有百分之40的几率出现跨行的item,这样就很好的保证了宽度不一的item随机出现!

所以宽度的代码是:

if (shtIndex < self.columnsCount - 1 && [self.COLUMNSHEIGHTS[shtIndex] floatValue] == [self.COLUMNSHEIGHTS[shtIndex+1] floatValue] && randomOfWhetherDouble < 40) {

size_width = 2*[self columnWidth];

}else{

size_width = [self columnWidth];

}

item.frame.size.height:

这个可以自由规定,因为在竖直方向高度没有特别的限制。比如我规定:

1.如果是横跨的,高度 = 宽度 * (0.75~1随机)

float extraRandomHeight = arc4random() % 25;

retVal = 0.75 + (extraRandomHeight / 100);

size_height = size_width * retVal;

2.如果是单列的,高度 = 宽度 * (0.75~1.25随机)

float extraRandomHeight = arc4random() % 50;

retVal = 0.75 + (extraRandomHeight / 100);

size_height = size_width * retVal; // 高度为宽度的0.75~1.25倍

补充

实际测试中发现,即使把出现横跨item的阈值调成0,也就是只要满足当前列和下一列高度相等,100%出现横跨的情况,出现横跨的情况也是少之又少。为什么呢?原因出在了数据类型上,之前我的用的数据类型全是CGFloat或者float的浮点类型,两个浮点数要相等的概率可想而知。改成NSUInteger之后就好多了。除此之外,为了增加横跨情况出现的概率,我还用到了四舍五入。拿图举个例子:

我们让item的高度对某个整数取余,比如以40为单位,让高度对40取余,再让item的高度剪掉这些余数。剩下的高度肯定是40的整数倍。

代码很简单:

size_height = size_height - (size_height % 40);

这可以把某个范围内的高度都归约到用一个高度,也就让左右两列高度相等的概率增加了,出现横跨item的可能性也变大了。

然后,在循环的过程中把每个item的frame赋值给对应的attributes,并把这些attributes保存到一个数组里。

//给attributes.frame 赋值,并存入 self.itemsAttributes

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

UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

attributes.frame = CGRectMake(origin_x, origin_y, size_width, size_height);

[self.itemsAttributes addObject:attributes];

然后在layoutAttributesForElementsInRect方法中返回:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{

return self.itemsAttributes;

}

最后

为了能让collectionView滑起来,我们还需要设置它的ContentSize.其实只要让ContentSize的高度变成COLUMNSHEIGHTS中最长列的高度即可。至于这个求数组中最长列的算法,和前面的求最短列类似。

-(CGSize)collectionViewContentSize{

CGSize size = self.collectionView.bounds.size;

NSUInteger longstIndex = [self findLongestColumn];

float columnMax = [self.COLUMNSHEIGHTS[longstIndex] floatValue];

size.height = columnMax;

return size;

}

效果

垂直滚动1

垂直滚动2

水平滚动1

水平滚动2

demo:

https://github.com/poisson-natsu/BHWAterFlow

用collectionview实现瀑布流-转(后面附demo,供参考)的更多相关文章

  1. 瀑布流原生ajax,demo

    最近听朋友们说起瀑布流挺多的,自己就去研究下了,一个简单的原生demo,分享给大家... 简单分为三个文档,有详细的注释 img:ajax.php:demo.php 其中img中放入图片 1.jpg: ...

  2. 用collectionview实现瀑布流-转

    算法总体思路 先说一下总体上的思路.既然图片的大小.位置各不一样,我们很自然地会想到需要算出每个item的frame,然后把这些frame赋值给当前item的UICollectionViewLayou ...

  3. CollectionView水平和竖直瀑布流的实现

    最近在项目中需要实现一个水平的瀑布流(即每个Cell的高度是固定的,但是长度是不固定的),因为需要重写系统 UICollectionViewLayout中的一些方法通过计算去实现手动布局,所以本着代码 ...

  4. 【iOS开发】collectionView 瀑布流实现

    一.效果展示 二.思路分析 1> 布局的基本流程 当设置好collectionView的布局方式之后(UICollectionViewFlowLayout),当系统开始布局的时候,会调用 pre ...

  5. 转载—— android 瀑布流的实现详解,附源码

    介绍 参考自:https://github.com/dodola/android_waterfall,因为原来的代码封装不好,所以,我根据源码的思路,重新写了一遍,所以有了现在这个项目:https:/ ...

  6. javacript 实现瀑布流原理和效果, 滚动加载图片【图文解析 附源码】

    先科普下瀑布流吧 瀑布流,又称瀑布流式布局.是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部.最早采用此布局的网站是Pin ...

  7. IOS 瀑布流UICollectionView实现

    IOS 瀑布流UICollectionView实现 在实现瀑布流之前先来看看瀑布流的雏形(此方法的雏形 UICollectionView) 对于UICollectionView我们有几点注意事项 它和 ...

  8. 用jquery实现瀑布流案例

    一.瀑布流是我们常见的案例,这里主要讲述,用jquery的方式实现瀑布流的功能! 引言:我们经常见到很多网站的瀑布流功能,如淘宝.京东这些商品等等.. 实现它我们首先考虑几个问题:1.获取到数据   ...

  9. iOS瀑布流实现(Swift)

    这段时间突然想到一个很久之前用到的知识-瀑布流,本来想用一个简单的方法,发现自己走入了歧途,最终只能狠下心来重写UICollectionViewFlowLayout.下面我将用两种方法实现瀑布流,以及 ...

随机推荐

  1. ASP.NET Aries JSAPI 文档说明:AR.DataGrid、AR.Dictionary

    AR.Global 文档 1:对象或属性: 名称 类型 说明 DG 对象 DataGrid操作对象 //datagrid集合,根据ID取出DataGrid对象,将Json当数组用. Items: ne ...

  2. opengl 笔记(2)

    /*- * Opengl Demo Test * * Fredric : 2016-7-10 */ #include <GLUT/GLUT.h> #include <stdlib.h ...

  3. 修复IE11首页被恶意篡改的问题

    前几天为了测试一个程序,把系统换成了Windows10,Windows10自带了微软的新浏览器Edge和IE11,用来其实也没什么太多感觉了,我习惯于Chrome,但有些东西还是得用IE,比如网银之类 ...

  4. 自己写jquery插件之模版插件高级篇(一)

    需求场景 最近项目改版中,发现很多地方有这样一个操作(见下图gif动画演示),很多地方都有用到.这里不讨论它的用户体验怎么样. 仅仅是从复用的角度,如果每个页面都去写text和select元素,两个b ...

  5. [翻译]AKKA笔记 - 有限状态机 -1

    原文地址:http://rerun.me/2016/05/21/akka-notes-finite-state-machines-1/ 我最近有个机会在工作上使用了Akka FSM,是个非常有趣的例子 ...

  6. selenium自动化基础知识

    什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...

  7. UML

    UML:1.继承关系用空心三角形+实线来表示2.实现接口用空心三角形+虚线来表示3.关联关系用实线箭头来表示4.依赖关系用虚线箭头来表示5.聚合关系用空心菱形+实线箭头来表示6.组合关系用实心菱形+实 ...

  8. css权威指南-基本视觉格式化(水平与垂直)

    1.基本概念     (1)正常流:是指西方语言文本从左向右,从上向下显示.如果要让一个元素不在正常流中国,唯一的办法                     就是使之成为浮动或定位元素.     ( ...

  9. ElasticSearch 5学习(8)——分布式文档存储(wait_for_active_shards新参数分析)

    学完ES分布式集群的工作原理以及一些基本的将数据放入索引然后检索它们的所有方法,我们可以继续学习在分布式系统中,每个分片的文档是被如何索引和查询的. 路由 首先,我们需要明白,文档和分片之间是如何匹配 ...

  10. ArrayAdapter

    Android Adapter:ArrayAdapter篇 版权声明:本文为博主原创文章,未经博主允许不得转载.微博:厉圣杰源码:AndroidDemo/Notification文中如有纰漏,欢迎大家 ...