前言 :

在写这篇文章之前, 先祝贺自己, 属于我的GitHub终于来了. 这也是我的GitHub的第一份代码, 以下文章的代码均可以在Demo clone或下载. 欢迎大家给予意见. 觉得写得不错的也请不要吝惜你们的star.


瀑布流

先普及下什么叫瀑布流

瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。


UICollectionView

我们知道, UICollectionView是苹果推出的继UITableView的又一个非常十分及其牛逼的UI控件, 除了有UITableView的缓存池, 重用机制外, 更能由你自主打造item(tableView中称cell, collectionView中称item)的显示布局, 只要你给它传一个layout布局属性, 他就能按照你的意愿去显示item.


UICollectionViewLayout

这里我就用自定义类JRWaterFallLayout继承UICollectionViewLayout来写瀑布流布局.

  • 需要手动实现的4个方法

方法 说明
– (void)prepareLayout collectionView第一次布局的时候和布局失效的时候会调用该方法, 需要注意的是子类记得要调用super
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect 返回rect范围内所有元素的布局属性的数组
– (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 返回indexPath位置上的元素的布局属性
– (CGSize)collectionViewContentSize 返回collectionView的滚动范围

注意 : 因为layoutAttributesForElementsInRect方法调用十分频繁, 所以布局属性的数组应该只计算一次保存起来而不是每次调用该方法的时候重新计算


 

代理

提供接口给外界修改一些瀑布流布局的参数, 例如显示的列数, 列距, 行距, 边缘距(UIEdgeInsets), 假如代理不实现该方法, 则用默认的参数.. 最最重要的还是item的高度!! 因为每个图片(item)的高度是由图片的宽高比和itemWidth来共同决定的. 所以itemHeight必须由代理来决定.这里展示几个代理方法 :

代理方法 说明
@required  
– (CGFloat)waterFallLayout:(JRWaterFallLayout *)waterFallLayout heightForItemAtIndex:(NSUInteger)index width:(CGFloat)width 返回index位置下的item的高度
@optional  
– (NSUInteger)columnCountOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout 返回瀑布流显示的列数
– (CGFloat)rowMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout 返回行间距
– (CGFloat)columnMarginOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout 返回列间距
– (UIEdgeInsets)edgeInsetsOfWaterFallLayout:(JRWaterFallLayout *)waterFallLayout 返回边缘间距

注意 : 由于上面所说的, layoutAttributesForElementsInRect方法调用十分频繁, 所以代理方法势必也会频繁调用. 但是并不是所有代理方法都是@required的, 所以在调用@optional的代理方法时需要如下代码那样每次都判断代理是否响应了该选择子, 以防代理没有实现该方法, 调用导致程序崩溃

if ( [self.delegate

respondsToSelector:@selector(columnCountOfWaterFallLayout:)] {

_columnCount = [self.delegate columnCountOfWaterFallLayout:self];

} else {

_columnCount = JRDefaultColumnCount;

}

每次都这样判断显然效率很低, 我们可以在prepareLayout方法中进行一次性判断, 然后用一个flags结构体存储起来, 那么下次我们在调用的时候直接对flag进行判断即可. 如下 :

struct { // 记录代理是否响应选择子

BOOL didRespondColumnCount : 1; // 这里的1是用1个字节存储

BOOL didRespondColumnMargin : 1;

BOOL didRespondRowMargin : 1;

BOOL didRespondEdgeInsets : 1;

} _delegateFlags;-

(void)setupDelegateFlags{

_delegateFlags.didRespondColumnCount = [self.delegate respondsToSelector:@selector(columnCountOfWaterFallLayout:)];

_delegateFlags.didRespondColumnMargin = [self.delegate respondsToSelector:@selector(columnMarginOfWaterFallLayout:)];

_delegateFlags.didRespondRowMargin = [self.delegate respondsToSelector:@selector(rowMarginOfWaterFallLayout:)];

_delegateFlags.didRespondEdgeInsets = [self.delegate respondsToSelector:@selector(edgeInsetsOfWaterFallLayout:)];

}

// 那么下次调用方法的时候就变成下面那么优雅了

_columnCount = _delegateFlags.didRespondColumnCount ? [self.delegate columnCountOfWaterFallLayout:self] : JRDefaultColumnCount;


整个瀑布流layout最重要的是找到item摆放的位置. 正是layoutAttributesForItemAtIndexPath方法要做的是. 下面开始说说找这个item位置的思路


瀑布流layout思路

这里本人一共需要用到2个可变数组和一个assign属性, 一个用来记录每列的高度, 一个用来记录所有itemAttributes. assign用来记录高度最大的列的高度

/** itemAttributes数组 */

@property (nonatomic, strong) NSMutableArray *attrsArray;

/** 每列的高度数组 */

@property (nonatomic, strong) NSMutableArray *columnHeights;

/** 最大Y值 */

property (nonatomic, assign) CGFloat maxY;

而在prepareLayout方法中, 以上2个数组都是要清空的, 因为网络请求的新数据到了, collectionView要重新布局的时候如果不清空, 继续往里边加东西的话, 会导致item的布局就全部乱套了..

接下里要处理的就是layoutAttributesForItemAtIndexPath方法中每个item该怎么布局了. 思路很简单

  • 创建一个UICollectionViewLayoutAttributes对象

  • 根据collectionView的width及行间距等几个参数, 计算出item的宽度

  • 找到最短列的列号

  • 根据列号计算item的x值, y值, 询问代理拿到item的高度

  • 设置UICollectionViewLayoutAttributes对象的frame属性

  • 返回UICollectionViewLayoutAttributes对象

问题主要出在怎么计算出x, y, width, height上. 看图说话.

详细的计算步骤可以看Demo.


最后

理论上只要你有足够强大的算法计算能力, 什么显示布局都能写出来. collectionViewLayout并不止于瀑布流!

iOS教你轻松打造瀑布流Layout的更多相关文章

  1. 手把手教你js原生瀑布流效果实现

    手把手教你js原生瀑布流效果实现 什么是瀑布流效果 首先,让我们先看一段动画: 在动画中,我们不难发现,这个动画有以下特点: 1.所有的图片的宽度都是一样的 2.所有的图片的高度是不一样的 3.图片一 ...

  2. iOS 两个tableview的 瀑布流

    iOS 两个tableview的 瀑布流1. [代码]Objective-C     ////  DocViewController.m//  getrightbutton////  Created ...

  3. iOS GZWaterfall任何形式的瀑布流

    概述 使用UICollectionView可以布局各种各样的瀑布流,下面我写了几种不同布局的瀑布流样式 详细 代码下载:http://www.demodashi.com/demo/11018.html ...

  4. MVC借助Masonry实现图文瀑布流

    借助Masonry可轻松实现瀑布流.本篇实现一个简单的图文瀑布流效果,如下: 图文瀑布流显示的2个要素是图片路径和文字内容,对应的Model为: namespace MvcApplication1.M ...

  5. iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流

    在上一篇博客中<iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流>,自定义瀑布流的列数,Cell的外边距,C ...

  6. iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流

    上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...

  7. IOS 瀑布流

    本篇博客应该算的上CollectionView的高级应用了,从iOS开发之窥探UICollectionViewController(一)到今天的(五),可谓是由浅入深的窥探了一下UICollectio ...

  8. iOS开发笔记15:地图坐标转换那些事、block引用循环/weak–strong dance、UICollectionviewLayout及瀑布流、图层混合

    1.地图坐标转换那些事 (1)投影坐标系与地理坐标系 地理坐标系使用三维球面来定义地球上的位置,单位即经纬度.但经纬度无法精确测量距离戒面积,也难以在平面地图戒计算机屏幕上显示数据.通过投影的方式可以 ...

  9. iOS之简单瀑布流的实现

    iOS之简单瀑布流的实现   前言 超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里. 实现思路 collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionVie ...

随机推荐

  1. phpcms 如何获取文章

    请求地址http://127.0.0.1/phpcms/index.php?m=content&c=index&a=show&catid=6&id=8 先来判断地址对应 ...

  2. python自动开发之第十二天

    一.数据库的介绍 (1)由多张表组成(2)存取有规则,数据有关联(3)数据量大,被优化 好处:更有效的存取数据 二.关系型数据库管理系统(RDBMS) Oracle,Mysql,Sqlserver,D ...

  3. J - 计算两点间的距离

      Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   Description 输入两 ...

  4. Ubuntu下安装和配置Apache2

    http://www.blogjava.net/duanzhimin528/archive/2010/03/05/314564.html 在Ubuntu中安装apache 安装指令:sudo apt- ...

  5. Expert Shell Scripting

    Expert Shell Scripting 好好学习这本书

  6. [BZOJ 1033] [ZJOI2008] 杀蚂蚁antbuster 【模拟!】

    题目链接: BZOJ - 1033 题目分析 模拟!纯粹按照题目描述模拟! 这是一道喜闻乐见的经典模拟题! 我一共写了2遍,Debug 历时2天的所有晚自习 ... 时间超过 8h ... 我真是太弱 ...

  7. [BZOJ 1070] [SCOI2007] 修车 【费用流】

    题目链接:BZOJ - 1070 题目分析 首先想到拆点,把每个技术人员拆成 n 个点,从某个技术人员拆出的第 i 个点,向某辆车连边,表示这是这个技术人员修的倒数第 i 辆车.那么这一次修车对整个答 ...

  8. Unity3d 项目管理

    Unity3d  项目管理 1.项目管理采用TortoiseSVN方式进行管理,但是也要结合人员管理的方式,尽量在U3D中多采用Scene(关卡的方式),以一下目录的方式进行管理! 以名字的方式进行合 ...

  9. PSoC电容式触摸感应技术

    PSoC是由Cypress半导体公司推出的具有数字和模拟混合处理能力的可编程片上系统芯片,某些系列的PSoC(如CY8C21X34系列),由于其内部配备的特殊资源,使得它可以很容易地实现电容式触摸感应 ...

  10. Dll方式的线程,需要引用这个

    {== D6DLLSynchronizer =================================================} {: This unit handles the D6 ...