前言 :

在写这篇文章之前, 先祝贺自己, 属于我的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. CHROME下载地址

    Chrome官方独立中文安装包下载地址 一般我们安装Google Chrome浏览器都是访问 http://www.google.com/chrome/?hl=zh-CN 然后下载运行ChromeSe ...

  2. 使用OpenSSL API进行安全编程

    http://www.ibm.com/developerworks/cn/linux/l-openssl.html OpenSSL API 的文档有些含糊不清.因为还没有多少关于 OpenSSL 使用 ...

  3. javabean 简介

    javabean其实包含多个方面的含义.   Java语言开发的可重用组件 优点:1,代码简洁.2,HTML与Java分离,好维护.3,将常用程序写成可重用组件,避免重复.   特点:1,所有类放在同 ...

  4. [HDOJ 1171] Big Event in HDU 【完全背包】

    题目链接:HDOJ - 1171 题目大意 有 n 种物品,每种物品有一个大小和数量.要求将所有的物品分成两部分,使两部分的总大小尽量接近. 题目分析 令 Sum 为所有物品的大小总和.那么就是用给定 ...

  5. OLAP 大表和小表并行hash join

    一个表50MB 一个表10GB 50M表做驱动表,放在PGA里 这时候慢在对对 10g 的全表扫描 对10个G扫描块 需要开并行 我有这样一个算法 一个进程 读 50mb 8进程 来 扫描 10gb ...

  6. 利用函数索引优化<>

    SQL> select count(*),ID from test_2 group by id; COUNT(*) ID ---------- ---------- 131072 1 11796 ...

  7. Linux kernel ‘fib6_add_rt2node’函数安全漏洞

    漏洞名称: Linux kernel ‘fib6_add_rt2node’函数安全漏洞 CNNVD编号: CNNVD-201307-265 发布时间: 2013-07-16 更新时间: 2013-07 ...

  8. id有空格获取不到元素

  9. 【REST API】

    微信公众平台开发者文档 RESTful API 设计最佳实践 登录判断写去接口里 PUT 新建一个资源POST 更新一个资源GET 查看一个资源DELETE 删除一个资源

  10. Evaluate Reverse Polish Notation——LeetCode

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...