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之简单瀑布流的实现的更多相关文章

  1. WPF下制作的简单瀑布流效果

    最近又在搞点小东西,美化界面的时候发现瀑布流效果比较不错.顺便就搬到了WPF,下面是界面 我对WEB前端不熟,JS和CSS怎么实现的,我没去研究过,这里就说下WPF的实现思路,相当简单. 1.最重要的 ...

  2. IOS开发之瀑布流照片墙实现

    想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳.现在网上流行一种叫做“瀑布流”的照片布局样式,这种行与列参差不齐的状态着实 ...

  3. jquery实现简单瀑布流

    瀑布流这个概念一直不是很理解,看到别人可以实现,自己弄了很久还是不能实现就很纠结.瀑布流这根刺就一直扎在我心里,一次偶然的机会看到别人实现了瀑布流,我想我是不是也应该再继续把这个未完成的任务画一个圆满 ...

  4. Jquery简单瀑布流代码示例

    最近很多网站都采用瀑布流风格设计,感觉挺有个性的,比较合适做图片类型的网站,没事仿开心网做一个瀑布流示例. 需要用到Jquery,jquery.masonry.min.js <!DOCTYPE ...

  5. jquery实现简单瀑布流代码

    测试环境:ie8 ff13.0.1  chrome22 可以将分页获取的内容依次填入四个div中,瀑布流的分页可以以多页(比如5页)为单位二次分页,这样可以减少后台算法的复杂度 <!DOCTYP ...

  6. javascript-实现简单瀑布流

    直接上代码: <style> *{ margin:0; padding:0; } .waterfall_item{ border:solid 3px #ccc; box-shadow:1p ...

  7. iOS UITabView简写瀑布流

    代码demo 一.tabViewCell,通过image的比例高算出cell 的高度 #import "TableViewCell.h" @implementation Table ...

  8. iOS开发-UICollectionView实现瀑布流

    关于瀑布流的实现网上有很多种解法,自定义控件,TableView+ScrollView,UICollectionView是iOS6发布之后用于展示集合视图,算起来已经发布三年左右了,不过知识点是不变的 ...

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

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

随机推荐

  1. CF911F Tree Destruction (树的直径,贪心)

    题目链接 Solution 1.先找出树的直径. 2.遍历直径沿途的每一个节点以及它的子树. 3.然后对于每个非直径节点直接统计答案,令直径的两个端点为 \(x_1,x_2\) . \[Ans=\su ...

  2. yield的概念及使用姿势

    概念: 当调用Thread.yield方法时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示. 代码演示: public class YieldDemo impl ...

  3. Java面试题之红黑树原理

    红黑树原理: 每个节点都只能是红色或黑色的: 根节点是黑色的: 每个叶节点(空节点)是黑色的: 如果一个节点是红色的,那么他的子节点都是黑色的: 从任意一个节点到其每个子节点的路径都有相同数目的黑色节 ...

  4. [bzoj2302][HNOI2011]problem c 递推,dp

    [HAOI2011]Problem c Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 949  Solved: 519[Submit][Status] ...

  5. ADO:用代码调用存储过程

    原文发布时间为:2008-08-02 -- 来源于本人的百度文章 [由搬家工具导入] using System;using System.Data;using System.Configuration ...

  6. DataSet的Merge方法合并两张表

    原文发布时间为:2008-08-01 -- 来源于本人的百度文章 [由搬家工具导入] UniqueConstraint uc = new UniqueConstraint("pk" ...

  7. set up trace code tool

    這以 GNU GLOBAL 6.5.6 為示範 1: install GNU GLOBAL https://www.gnu.org/software/global/download.html sudo ...

  8. LightOJ 1140: How Many Zeroes? (数位DP)

    当前数位DP还不理解的点: 1:出口用i==0的方式 2:如何省略状态d(就是枚举下一个数的那个状态.当然枚举还是要的,怎么把空间省了) 总结: 1:此类DP,考虑转移的时候,应当同时考虑查询时候的情 ...

  9. MinGW 使用和创建 DLL 应注意的问题

    MinGW 是 GCC 的 Windows 版本,稳定版已经到了 4.5.2,功能和性能上很好,感觉不比 Microsoft 自家的 VC 差啊.但是 MinGW 下使用和创建 DLL 倒是要特别注意 ...

  10. AC日记——最小路径覆盖问题 洛谷 P2764

    题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...