iOS之简单瀑布流的实现
iOS之简单瀑布流的实现
前言
超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里。
实现思路
collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionViewLayout,因此我们要自定义一个layout来继承系统的UICollectionViewLayout,所有工作都在这个类中进行。
1.定义所需属性
瀑布流的思路就是,从上往下,那一列最短,就把下一个item放在哪一列,因此我们需要定义一个字典来记录每一列的最大y值
每一个item都有一个attributes,因此定义一个数组来保存每一个item的attributes。
我们还必须知道有多少列以及列间距、行间距、section到collectionView的边距。
//总列数
@property (nonatomic, assign) NSInteger columnCount;
//列间距
@property (nonatomic, assign) NSInteger columnSpacing;
//行间距
@property (nonatomic, assign) NSInteger rowSpacing;
//section到collectionView的边距
@property (nonatomic, assign) UIEdgeInsets sectionInset;
//保存每一列最大y值的数组
@property (nonatomic, strong) NSMutableDictionary *maxYDic;
//保存每一个item的attributes的数组
@property (nonatomic, strong) NSMutableArray *attributesArray;
2.重写系统方法
我们一共需要重写4个方法
1)- (void)prepareLayout
2)- (CGSize)collectionViewContentSize
3)- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
4)- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
- (void)prepareLayout 方法
布局前的一些准备工作都在这里进行,初始化字典,有几列就有几个键值对,key为第几列,value为列的最大y值,初始值为上内边距:
for
(int i = 0; i < self.columnCount; i++) {
self.maxYDic[@(i)] = @(self.sectionInset.top);
}
创建每个item的attributes,并存入数组:
//根据collectionView获取总共有多少个item
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
//为每一个item创建一个attributes并存入数组
for
(int i = 0; i < itemCount; i++) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attributesArray addObject:attributes];
}
- (CGSize)collectionViewContentSize 方法
用来计算collectionView的contentSize
一般瀑布流只能垂直滚动,不能水平滚动,因此contentSize.width = 0,我们只需要计算contentSize.height即可
从字典中找出最长列的最大y值,再加上下面的内边距,即为contentSize.height
- (CGSize)collectionViewContentSize {
//假设第0列是最长的那列
__block NSNumber *maxIndex = @0;
//遍历字典,找出最长的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
//如果maxColumn列的最大y值小于obj,则让maxColumn等于obj所属的列
if
([self.maxYDic[maxIndex] floatValue] < obj.floatValue) {
maxIndex = key;
}
}];
//collectionView的contentSize.height就等于最长列的最大y值+下内边距
return
CGSizeMake(0, [self.maxYDic[maxIndex] floatValue] + self.sectionInset.bottom);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 方法
该方法则用来设置每个item的attributes,在这里,我们只需要简单的设置每个item的attributes.frame即可
首先我们必须得知collectionView的尺寸,然后我们根据collectionView的宽度,以及列数、各个间距来计算每个item的宽度
item的宽度 = (collectionView的宽度 - 内边距及列边距) / 列数
CGFloat collectionViewWidth = self.collectionView.frame.size.width;
//self.sectionInset.left:左边距 self.sectionInset.right:右边距
//(self.columnCount - 1) * columnSpacing:一行中所有的列边距
CGFloat itemWidth = (collectionViewWidth - self.sectionInset.left - self.sectionInset.right - (self.columnCount - 1) * self.columnSpacing) / self.columnCount;
接下来计算item的坐标,要想计算坐标,那就必须知道最短的那一列,先遍历字典,找出最短列是哪一列(minColumn)以及其最大y值。
item的y值就等于最短列的最大y值再加上行间距,x值就等于左边距 + (item宽度 + 列间距) * minColumn
//找出最短的那一列
__block NSNumber *minIndex = @0;
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
if
([self.maxYDic[minIndex] floatValue] > obj.floatValue) {
minIndex = key;
}
}];
//根据最短列的列数计算item的x值
CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;
//item的y值 = 最短列的最大y值 + 行间距
CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;
接下来便是item的高度,我们应该根据图片的原始尺寸以及计算出来的宽度,等比例缩放来计算高度,但是在layout类中,我们是拿不到图片的,因此我们可以定义一个block属性,或者代理,让外界来计算并返回给我们,我们需要将item的宽度以及indexPath传递给外界:
@property (nonatomic, strong) CGFloat(^itemHeightBlock)(CGFloat itemHeight,NSIndexPath *indexPath);
根据返回值来设置item的高度:
if
(self.itemHeightBlock) itemHeight = self.itemHeightBlock(itemWidth, indexPath);
最后设置attributes的frame并更新字典:
//设置attributes的frame
attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);
//更新字典中的最短列的最大y值
self.maxYDic[minIndex] = @(CGRectGetMaxY(attributes.frame));
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 方法
该方法用来返回rect范围内,item的attributes
直接返回attributesArray即可
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return
self.attributesArray;
}
使用
布局类写完了,接下来就可以直接使用了
//创建布局对象
XRWaterfallLayout *waterfall = [[XRWaterfallLayout alloc] init];
//设置相关属性
waterfall.columnCount = 3;
//共多少列
waterfall.columnSpacing = 10;
//列间距
waterfall.rowSpacing = 10;
//行间距
waterfall.sectionInset = UIEdgeInsetsMake(10, 10 , 10, 10);
//内边距
[waterfall setItemHeightBlock:^CGFloat(CGFloat itemWidth, NSIndexPath *indexPath) {
//根据图片的原始尺寸,及显示宽度,等比例缩放来计算显示高度
XRImage *image = self.images[indexPath.item];
return
image.imageH / image.imageW * itemWidth;
}];
collectionView.collectionViewLayout = waterfall;
......
......
具体代码请到这里下载:https://github.com/codingZero/XRWaterfallLayout,觉得不错的,请献上你的star
iOS之简单瀑布流的实现的更多相关文章
- WPF下制作的简单瀑布流效果
最近又在搞点小东西,美化界面的时候发现瀑布流效果比较不错.顺便就搬到了WPF,下面是界面 我对WEB前端不熟,JS和CSS怎么实现的,我没去研究过,这里就说下WPF的实现思路,相当简单. 1.最重要的 ...
- IOS开发之瀑布流照片墙实现
想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳.现在网上流行一种叫做“瀑布流”的照片布局样式,这种行与列参差不齐的状态着实 ...
- jquery实现简单瀑布流
瀑布流这个概念一直不是很理解,看到别人可以实现,自己弄了很久还是不能实现就很纠结.瀑布流这根刺就一直扎在我心里,一次偶然的机会看到别人实现了瀑布流,我想我是不是也应该再继续把这个未完成的任务画一个圆满 ...
- Jquery简单瀑布流代码示例
最近很多网站都采用瀑布流风格设计,感觉挺有个性的,比较合适做图片类型的网站,没事仿开心网做一个瀑布流示例. 需要用到Jquery,jquery.masonry.min.js <!DOCTYPE ...
- jquery实现简单瀑布流代码
测试环境:ie8 ff13.0.1 chrome22 可以将分页获取的内容依次填入四个div中,瀑布流的分页可以以多页(比如5页)为单位二次分页,这样可以减少后台算法的复杂度 <!DOCTYP ...
- javascript-实现简单瀑布流
直接上代码: <style> *{ margin:0; padding:0; } .waterfall_item{ border:solid 3px #ccc; box-shadow:1p ...
- iOS UITabView简写瀑布流
代码demo 一.tabViewCell,通过image的比例高算出cell 的高度 #import "TableViewCell.h" @implementation Table ...
- iOS开发-UICollectionView实现瀑布流
关于瀑布流的实现网上有很多种解法,自定义控件,TableView+ScrollView,UICollectionView是iOS6发布之后用于展示集合视图,算起来已经发布三年左右了,不过知识点是不变的 ...
- iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流
上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...
随机推荐
- bzoj1143/2718 祭祀river(最大独立集)
[CTSC2008]祭祀river Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2175 Solved: 1098[Submit][Status] ...
- jenkins配置发送邮件
1.打开系统管理->系统设置,找到邮件设置,如下: 2.SMTP或者其他方式的发送邮件,可自行配置,一下列出了qq邮箱和163邮箱设置的地方,如下图: qq邮箱: 往下拉,找到如下图: 163邮 ...
- 实时监控linux
https://www.cnblogs.com/tinywan/p/6826125.html
- Tomcat和JVM的性能调优总结
Tomcat性能调优: 找到Tomcat根目录下的conf目录,修改server.xml文件的内容.对于这部分的调优,我所了解到的就是无非设置一下Tomcat服务器的最大并发数和Tomcat初始化时创 ...
- 如何应用r.js对requirejs下的js代码合并
1.在根目录新建build.js ({ baseUrl:'js', paths:{ jquery:'static/jquery-1.10.2.min', underscore:'static/unde ...
- Linux--内核Uevent事件机制 与 Input子系统【转】
转自:http://blog.csdn.net/lxl584685501/article/details/46379453 [-] 一Uevent机制 Uevent在kernel中的位置 Uevent ...
- php 生成二维码图片
php 生成二维码图片 (1)下载类库文件 php类库PHP QR Code,地址:http://phpqrcode.sourceforge.net/. (2)放到项目里 把下载的文件解压后有个php ...
- Day 18 函数之一
函数参数: 1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元.因此,形参只在函数内部有效.函数调用结束返回主调用函数后则不能再使用该形参变量 2.实参可以是常量.变量. ...
- HDU 1011 Starship Troopers (树dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 题意: 题目大意是有n个房间组成一棵树,你有m个士兵,从1号房间开始让士兵向相邻的房间出发,每个 ...
- 3.4 熟练掌握动态规划——状态压缩DP
从旅行商问题说起—— 给定一个图,n个节点(n<=15),求从a节点出发,经历每个节点仅一次,最后回到a,需要的最短时间. 分析: 设定状态S代表当前已经走过的城市的集合,显然,S<=(1 ...