CHTCollectionViewWaterfallLayout分析及仿写优化

瀑布流已经是现在很常用的布局样式,用collectionView就可以很方便的实现,现在github上star最多的就是这个CHTCollectionViewWaterfallLayout,也是我项目中经常用的。
时间长了还是发现有满足不了需求的地方(比如始终缺失的像tableViewHeader一样的collectionViewHeader和collectionViewFooter),
还发现了个bug:当section个数大于1但第一个section中item个数为0时,第二个section中item的位置会出现错误(原因是当item==0时依然减去了itemSpacing,给他提了issue,但是没处理)
再加上其Swift版的库一直没更新,
所以还是决定自己仿写一个,学习一下大神的思路,顺便修复发现的bug,加上自己需要的header和footer,整理下代码结构。
地址在这里:https://github.com/Phelthas/LXMWaterfallLayout
效果如图:

CHTCollectionViewWaterfallLayout分析

CHTCollectionViewWaterfallLayout的大致思路是:
每个section有几个column是固定的,然后根据insect,spacing等属性,就可以计算出每一列的宽度,然后根据delegate返回的itemSize,就可以按比例缩放出实际显示的itemSize。
然后根据renderDirection属性,确定每个item的位置。
CHTCollectionViewWaterfallLayout想做一个跟UICollectionViewFlowLayout一样简单易用的类,所以整个结构就是仿照UICollectionViewFlowLayout来的,属性名称,协议名称等也是,所以看起来非常舒服。

首先看协议


跟UICollectionViewDelegateFlowLayout几乎一模一样,不过sizeForItem变成了required,referenceSize变成了height。
referenceSize变成height我觉得很合理,因为实际用过程中也只会用到height,width肯定是collectionView的宽度;
sizeForItem我觉得可以不用必须,如果没有设置,那按照计算出来的columnWidth设置成正方形就行了嘛,就像UICollectionViewFlowLayout一样也有个初始值。

再看属性


也跟UICollectionViewDelegateFlowLayout几乎一模一样,多了个columnCount,多了个itemRenderDirection,referenceSize变成了height,还多了minimumContentHeight和一个辅助方法。没啥好分析的。

还有私有属性


协议是继承UICollectionViewDelegate的,所以layout的delegate是取self.collectionView.delegate作为layout的delegate,用起来跟UICollectionViewFlowLayout完全一样,舒服~
columnHeights是个二维数组,保存每个section中每个column的高度,注意是具体某一列的累计高度,不是某个cell或者item的高度!!!这个很重要
unionRects是个比较难理解的东西,作者用它来优化layoutAttributesForElementsInRect这个方法,我个人感觉意义不大。。。或许是没领会到作者的深意吧。。。
headerAttribute和footerAttribute作者用了字典而不是二维数组,key是对应的section。
其他属性也都很好理解,不说了。

然后看实现

最重要的就是这个prepareLayout这个方法。
最开始初始化各个数组,然后开始遍历section计算所有的layoutAttributes。
1, 首先是sectionHeader,CHTCollectionViewWaterfallLayout是让sectionHeader也受sectionInset影响的,所以sectionHeader的起始x是sectionInset.left, 起始y是sectionInset.top;
而UICollectionViewFlowLayout的sectionHeader是不受sectionInset影响的,所以起始x是0,起始y也是0;
两种方式都有有道理,我更倾向于UICollectionViewFlowLayout的方式,因为section大小给大了,view可以用空白填补,但是要给小了想设置不用样式就难了,
所以我仿写的时候就就用UICollectionViewFlowLayout的方式了。

2, 然后是将columnHeights中对应section的数组的值全部设置为sectionHeader.fame.maxY。这一步是为了统一下面计算item位置的起始位置。
我觉得,既然是columnHeights,就不要把sectionHeader之类的高度也计算进去了,
所以仿写的时候采用了另外的计算方式:用一个私有变量contentHeight来保存当前的Y值,columnHeights只用来保存对应的column的高度,计算位置的时候将两者加起来即可

3, 然后是计算每个item的位置
先计算出item应该放在那一列,CHTCollectionViewWaterfallLayout用itemRenderDirection定义了三种排列方式:最短优先,从左往右,从右往左;
我感觉用处不是很大,既然是瀑布流了,那应该不太在意横着是怎么拍的,所以仿写的时候就只保留了最短优先这一种方式;
而确定哪个最短的方式也很简单,就是从columnHeights中找出最短的那一列的index;
然后创建attribute,根据indexPath设置frame,添加到数组即可
也就是这里,CHTCollectionViewWaterfallLayout有个bug:
如果某个section的item个数为0,那就不应该计算item的位置,而CHTCollectionViewWaterfallLayout目前的做法是在计算完成之后,判断columnHeights对应section的columnCount是否为0,
为0则再减去itemSpacing,问题是某个section的columnCount跟itemCount其实没啥关系,可能我计划这个section有2列,但是刚好没有数据,itemCount为0,这种情况下判断就出错了。。。
我仿写的做法是:把加spacing放在加item高度之前;首先判断item是否是该列的第一个,是第一个则不用加spacing,否则再加spacing;目前来看是很好的解决了这个问题~~

4, 然后是sectionFooter,方法同sectionHeader。

到这里其实整个布局所需要的属性就都已经计算出来了,下面只需要按需求重载UICollectionViewLayout的函数,返回对应数据即可。
这里CHTCollectionViewWaterfallLayout用了一个unionRect数据,貌似是用来优化计算:
写死了unionSize = 20,然后将所有的item分为几组,每组20个,计算出每组的unionRect,保存在数组中。

然后是需要重载的函数
- (CGSize)collectionViewContentSize;
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path;
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
以上这三个 根据上面的计算返回即可;
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
这个方法一般都是判断新旧bounds是否一样,一样则返回NO,不一样返回YES。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
是需要注意的,CHTCollectionViewWaterfallLayout也就是在这里用到了那个unionRects数组,
前后两个遍历找出所有与rect想交的rect,再返回需要的item的attributes
这里也是我最不明白的地方,感觉这个方法对效率的提升很有限呐,
反正我仿写的时候是直接遍历所有attributes数组,返回与rect相交的数组了。
有知道作者那么写有什么深意的,望不吝赐教~~~

LXMWaterfallLayout 改进及优化

1,完全是用Swift3.0写的,语法什么的应该都是最新的,没什么兼容性问题
2,加入了 collectionViewHeaderHeight和collectionViewFooterHeight两个属性,
用法同sectionHeader和sectionFooter,需要在collectionView注册nib或者class,然后在
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
方法中取出来设置即可
这里因为collectionView的布局完全受layout控制,所以像tableView一样直接设置为collectionView的属性是不行的,肯定会涉及到layout,目前只想到了这种方式,如果谁有什么好的想法,欢迎讨论~
3,加入了默认的itemSize实现
所有的协议方法默认都可以不实现,不实现的时候,就相当是一个每个section固定有几列,且支持collectionViewHeader和collectionViewFooter的layout
4,自以为代码写的还算比较规范,结构还算清晰,看起来比较舒服(⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容