目标效果

因为系统给我们提供的 UICollectionViewFlowLayout 布局类不能实现瀑布流的效果,如果我们想实现 瀑布流 的效果,需要自定义一个 UICollectionViewLayout  类,实现瀑布流效果。效果如右图。

依赖工具:

我们需要一个图片大小和图片地址的Josn数据, 和 SDWebImage图片加载的第三方工具

RootViewController.m

  1 #import "RootViewController.h"
2 #import "DataModel.h"
3 #import "WaterFlowLayout.h"
4 #import "RootCell.h"
5 #import "UIImageView+WebCache.h"
6
7 @interface RootViewController ()<UICollectionViewDataSource, UICollectionViewDelegate, WaterFlowLayoutDelegate>
8
9 // 声明大数组存放所有的数据
10 @property (nonatomic, strong) NSMutableArray *allDataArray;
11
12 // 定义collectionView
13 @property (nonatomic, strong) UICollectionView *collectionView;
14
15 @end
16
17 @implementation RootViewController
18
19 // 懒加载
20 - (NSMutableArray *)allDataArray {
21 if (!_allDataArray) {
22 _allDataArray = [NSMutableArray array];
23 }
24 return _allDataArray;
25 }
26
27 - (void)viewDidLoad {
28 [super viewDidLoad];
29 // Do any additional setup after loading the view.
30
31 // 读取数据
32 [self loadData];
33
34 // 初始化布局
35 [self initLayout];
36
37 // 注册cell
38 [self.collectionView registerClass:[RootCell class] forCellWithReuseIdentifier:@"cell"];
39 }
40
41 // 初始化布局
42 - (void)initLayout {
43
44 // 1.创建UICollectionView的布局样式对象
45 WaterFlowLayout *water = [[WaterFlowLayout alloc] init];
46 CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3;
47 water.itemSize = CGSizeMake(width, width);
48 // 设置内边距
49 water.sectionInsets = UIEdgeInsetsMake(10, 10, 10, 10);
50 // 设置间距
51 water.spacing = 10;
52 // 设置有多少列
53 water.numberOfColumn = 3;
54 // 设置代理
55 water.delegate = self;
56
57 // 2.布局UICollectionView
58 self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:water];
59 self.collectionView.delegate = self;
60 self.collectionView.dataSource = self;
61 self.collectionView.backgroundColor = [UIColor whiteColor];
62 [self.view addSubview:self.collectionView];
63
64 }
65
66 // 读取数据
67 - (void)loadData {
68
69 // 第一步:获取文件路径
70 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];
71 // 第二步:根据路径读取数据,转为NSData对象
72 NSData *data = [NSData dataWithContentsOfFile:filePath];
73 // 第三步:根据json格式解析数据
74 NSArray *dataArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
75
76 // NSLog(@"%@", dataArray);
77 // 第四步:遍历数组,将数据转为model对象
78 for (NSDictionary *dict in dataArray) {
79
80 // 创建model对象
81 DataModel *model = [[DataModel alloc] init];
82 // 使用KVC给model赋值
83 [model setValuesForKeysWithDictionary:dict];
84
85 // 将model添加到大数组中
86 [self.allDataArray addObject:model];
87 }
88 // NSLog(@"%@", self.allDataArray);
89 }
90
91 // 设置分区个数
92 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
93 return 1;
94 }
95 // 设置每个分区的item个数
96 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
97 return self.allDataArray.count;
98 }
99
100 // 返回每一个item的cell对象
101 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
102
103 RootCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
104
105 // 设置cell数据
106
107 DataModel *model = self.allDataArray[indexPath.row];
108 NSURL *url = [NSURL URLWithString:model.thumbURL];
109 [cell.photoView sd_setImageWithURL:url];
110 cell.backgroundColor = [UIColor orangeColor];
111 return cell;
112 }
113
114 // 实现代理方法返回每一个item的高度
115 - (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath {
116
117 DataModel *model = self.allDataArray[indexPath.row];
118 CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3;
119 // 计算item高度
120 CGFloat height = model.height / model.width * width;
121 return height;
122
123 }
124
125 @end

主视图文件,主要用于处理数据和布局页面

RootCell.h

1 #import <UIKit/UIKit.h>
2
3 @interface RootCell : UICollectionViewCell
4
5 @property (nonatomic, strong) UIImageView *photoView;
6
7 @end

RootCell.m

 1 #import "RootCell.h"
2
3 @implementation RootCell
4
5 - (instancetype)initWithFrame:(CGRect)frame {
6 self = [super initWithFrame:frame];
7 if (self) {
8 self.photoView = [[UIImageView alloc] init];
9 [self.contentView addSubview:self.photoView];
10 }
11 return self;
12 }
13
14 - (void)layoutSubviews {
15
16 self.photoView.frame = self.bounds;
17
18 }
19
20 @end

RootCell就是每一个Item的样式, 也就是一张张图片

WaterFlowLayout.h

 1 #import <UIKit/UIKit.h>
2
3 @protocol WaterFlowLayoutDelegate <NSObject>
4
5 // 返回每一个item的高度
6 - (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath;
7
8 @end
9
10 @interface WaterFlowLayout : UICollectionViewLayout
11
12 // item的大小,需要根据这个获取宽度
13 @property (nonatomic, assign) CGSize itemSize;
14
15 // 内边距的设置
16 @property (nonatomic, assign) UIEdgeInsets sectionInsets;
17
18 // item的间距(这里水平方向和垂直方向的间距一样)
19 @property (nonatomic, assign) CGFloat spacing;
20
21 // 列数
22 @property (nonatomic, assign) NSInteger numberOfColumn;
23
24 // 设置代理,用于获取item的高度
25 @property (nonatomic, weak) id<WaterFlowLayoutDelegate>delegate;
26
27 @end

WaterFlowLayout.m

  1 #import "WaterFlowLayout.h"
2
3 @interface WaterFlowLayout ()
4
5 // 声明私有属性
6 // 保存一共有多少个item
7 @property (nonatomic, assign) NSInteger numberOfItems;
8
9 // 保存计算好的每一个item的信息
10 @property (nonatomic, strong) NSMutableArray *itemAttributes;
11
12 // 保存每一列的高度
13 @property (nonatomic, strong) NSMutableArray *columnHeights;
14
15
16 // 声明私有方法
17 // 找到当前最长列
18 - (NSInteger)indexOfHeightestColumn;
19
20 // 找到当前最短列
21 - (NSInteger)indexOfShortestColumn;
22
23 @end
24
25 @implementation WaterFlowLayout
26
27 // 懒加载
28 - (NSMutableArray *)itemAttributes {
29 if (!_itemAttributes) {
30 _itemAttributes = [NSMutableArray array];
31 }
32 return _itemAttributes;
33 }
34
35 - (NSMutableArray *)columnHeights {
36 if (!_columnHeights) {
37 _columnHeights = [NSMutableArray array];
38 }
39 return _columnHeights;
40 }
41
42 // 找到当前最长列
43 - (NSInteger)indexOfHeightestColumn {
44 // 记录最长列的下标
45 NSInteger index = 0;
46 // 记录最长列的高度
47 CGFloat length = 0;
48 for (int i = 0; i < self.columnHeights.count; i++) {
49 // 将数组中的对象转为基本数值
50 CGFloat currentLength = [self.columnHeights[i] floatValue];
51 if (currentLength > length) {
52 length = currentLength;
53 index = i;
54 }
55 }
56 return index;
57 }
58
59 // 找到当前最短列
60 - (NSInteger)indexOfShortestColumn {
61 NSInteger index = 0;
62 CGFloat length = MAXFLOAT;
63 for (int i = 0; i < self.columnHeights.count; i++) {
64
65 CGFloat currentLength = [self.columnHeights[i] floatValue];
66 if (currentLength < length) {
67 length = currentLength;
68 index = i;
69 }
70 }
71 return index;
72 }
73
74 // 接下来重写三个方法
75
76 // 准备布局,在这里计算每个item的frame
77 - (void)prepareLayout {
78
79 // 拿到一共有多少个item
80 self.numberOfItems = [self.collectionView numberOfItemsInSection:0];
81 // 每一列添加一个top高度
82 for (int i = 0; i < self.numberOfColumn; i++) {
83 // @() NSNumber字面量创建对象
84 self.columnHeights[i] = @(self.sectionInsets.top);
85 }
86
87 // 依次为每个item设置位置信息,并存储在数组中
88 for (int i = 0; i < self.numberOfItems; i++) {
89
90 // 1.找到最短列的下标
91 NSInteger shortestIndex = [self indexOfShortestColumn];
92 // 2.计算X 目标X = 内边距左间距 + (宽 + item间距)*最短列下标
93 CGFloat detalX = self.sectionInsets.left + shortestIndex * (self.itemSize.width + self.spacing);
94 // 3.找到最短列的高度
95 CGFloat height = [self.columnHeights[shortestIndex] floatValue];
96 // 4.计算Y
97 CGFloat detalY = height + self.spacing;
98 // 5.创建indexPath
99 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
100
101 // 6.调用代理方法计算高度
102 CGFloat itemHeight = 0;
103 if (_delegate && [_delegate respondsToSelector:@selector(heightForItemAtIndexPath:)]) {
104 itemHeight = [_delegate heightForItemAtIndexPath:indexPath];
105 }
106
107 // 定义保存位置信息的对象
108 UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
109
110 // 7.生成frame
111 attributes.frame = CGRectMake(detalX, detalY, self.itemSize.width, itemHeight);
112 // 8.将位置信息添加到数组中
113 [self.itemAttributes addObject:attributes];
114
115 // 9.更新这一列的高度
116 self.columnHeights[shortestIndex] = @(detalY + itemHeight);
117 }
118
119 }
120
121 // 返回UICollectionView的大小
122 - (CGSize)collectionViewContentSize {
123
124 // 求最高列的下标
125 NSInteger heightest = [self indexOfHeightestColumn];
126 // 最高列的高度
127 CGFloat height = [self.columnHeights[heightest] floatValue];
128 // 拿到collectionView的原始大小
129 CGSize size = self.collectionView.frame.size;
130 size.height = height + self.sectionInsets.bottom;
131
132 return size;
133 }
134
135 // 返回每一个item的位置信息
136 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
137 return self.itemAttributes;
138 }
139
140 @end

WaterFlowLayout 就是我们自定义的 瀑布流 的文件

DataModel.h

 1 #import <Foundation/Foundation.h>
2
3 @interface DataModel : NSObject
4
5 @property (nonatomic, copy) NSString *thumbURL;
6
7 @property (nonatomic, assign) float width;
8
9 @property (nonatomic, assign) float height;
10
11
12 @end

DataModel.m

 1 #import "DataModel.h"
2
3 @implementation DataModel
4
5 // 防崩
6 - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
7
8 }
9
10 @end

iOS自定义UICollectionViewLayout之瀑布流的更多相关文章

  1. 自定义UICollectionViewLayout之瀑布流

    目标效果 因为系统给我们提供的 UICollectionViewFlowLayout 布局类不能实现瀑布流的效果,如果我们想实现 瀑布流 的效果,需要自定义一个 UICollectionViewLay ...

  2. 自定义UICollectionViewLayout 实现瀑布流

    今天研究了一下自定义UICollectionViewLayout. 看了看官方文档,要自定义UICollectionViewLayout,需要创建一个UICollectionViewLayout的子类 ...

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

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

  4. ios图文混编瀑布流

    ios图文混编瀑布流,利用UICollectionView 实现图文混编的瀑布流,支持section内容伸缩 http://www.huiyi8.com/pubuliu/

  5. iOS开发进阶 - 自定义UICollectionViewLayout实现瀑布流布局

    移动端访问不佳,请访问我的个人博客 最近项目中需要用到瀑布流的效果,但是用UICollectionViewFlowLayout又达不到效果,自己动手写了一个瀑布流的layout,下面是我的心路路程 先 ...

  6. OC - 29.自定义布局实现瀑布流

    概述 瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UIColl ...

  7. iOS---UICollectionView自定义流布局实现瀑布流效果

    自定义布局,实现瀑布流效果 自定义流水布局,继承UICollectionViewLayout 实现一下方法 // 每次布局之前的准备 - (void)prepareLayout; // 返回所有的尺寸 ...

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

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

  9. iOS开发:一个瀑布流的设计与实现(已实现缓存池功能,该功能使得瀑布流cell可以循环利用)

    一个瀑布流的实现有三种方式: 继承自UIScrollView,仿写UITableView的dataSource和delegate,创造一个缓存池用来实现循环利用cell 写多个UITableview( ...

随机推荐

  1. 函数 flst_get_first

    /********************************************************************//** Gets list first node addre ...

  2. 转自 Because of you 的总结

    上下界网络流的问题严格的分,可以分为四类吧. 1:无源汇可行流  sgu 194 2:有源汇可行流  poj 2396  这题比较好,我建图建了将近200行 3:有源汇最大流  zoj 3496  这 ...

  3. 【转】ubuntu11.10 64bit 环境android编译错误

    原文网址:http://blog.csdn.net/lmhgen/article/details/7326083 错误1: 注意:external/protobuf/java/src/main/jav ...

  4. ES6/ES2015核心内容

    ECMAScript定义了: JS语言语法 – 语法解析规则.关键字.语句.声明.运算符等. 类型 – 布尔型.数字.字符串.对象等. 原型和继承 内建对象和函数的标准库 – JSON.Math.数组 ...

  5. JavaScript里的类和继承

    JavaScript与大部分客户端语言有几点明显的不同: JS是 动态解释性语言,没有编译过程,它在程序运行过程中被逐行解释执行JS是 弱类型语言,它的变量没有严格类型限制JS是面向对象语言,但 没有 ...

  6. Linux查看系统性能命令

    性能调优的第一步是性能分析,下面从性能分析着手进行一些介绍,尤其对linux性能分析工具vmstat的用法和实践进行详细介绍. ———————————————————————————————————— ...

  7. HW输入字符串长度,字符串,计数m。从前往后计数,当数到m个元素时,m个元素出列,同时将该元素赋值给m,然后从下一个数计数循环,直到所有数字都出列,给定的数全部为大于0的数字。输出出队队列。

    package huawei; import java.util.Scanner; public class 约瑟夫环 { private static class Node { public int ...

  8. node系列1

    NodeJS基础 JS是脚本语言,脚本语言都需要一个解析器才能运行,NodeJS就是一个解析器.nodejs.org 打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果 ...

  9. SharePoint 命令使用集锦 (持续更新中...)

    首先要定位到 cd "Program Files\Common Files\Microsoft Shared\web server extensions\14\BIN" 下面   ...

  10. fckeditor的用法

    1.下载解压文件 2.将解压后的文件放入webroot下 3.将js引入要使用fckeditor的页面 4.用ckeditor替换textarea <@e.textarea id="t ...