iOS开发UI篇—自定义瀑布流控件(基本实现)
一、基本实现
说明:在View加载的时候,刷新数据。
1.实现代码
YYViewController.m文件
1 //
2 // YYViewController.m
3 // 06-瀑布流
4 //
5 // Created by apple on 14-7-28.
6 // Copyright (c) 2014年 wendingding. All rights reserved.
7 //
8
9 #import "YYViewController.h"
10 #import "YYWaterflowView.h"
11 #import "YYWaterflowViewCell.h"
12
13 @interface YYViewController ()<YYWaterflowViewDelegate,YYWaterflowViewDataSource>
14
15 @end
16
17 @implementation YYViewController
18
19 - (void)viewDidLoad
20 {
21 [super viewDidLoad];
22 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init];
23 waterflow.frame=self.view.bounds;
24 waterflow.delegate=self;
25 waterflow.dadaSource=self;
26 [self.view addSubview:waterflow];
27
28 //刷新数据
29 [waterflow reloadData];
30 }
31
32 #pragma mark-数据源方法
33 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView
34 {
35 return 100;
36 }
37 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView
38 {
39 return 3;
40 }
41 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index
42 {
43 YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init];
44 //给cell设置一个随机色
45 cell.backgroundColor=YYRandomColor;
46 return cell;
47 }
48
49
50 #pragma mark-代理方法
51 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index
52 {
53 switch (index%3) {
54 case 0:return 90;
55 case 1:return 110;
56 case 2:return 80;
57 default:return 120;
58 }
59 }
60 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type
61 {
62 switch (type) {
63 case YYWaterflowViewMarginTypeTop:
64 case YYWaterflowViewMarginTypeBottom:
65 case YYWaterflowViewMarginTypeLeft:
66 case YYWaterflowViewMarginTypeRight:
67 return 10;
68 case YYWaterflowViewMarginTypeColumn:
69 case YYWaterflowViewMarginTypeRow:
70 return 5;
71 }
72 }
73 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index
74 {
75 NSLog(@"点击了%d的cell",index);
76 }
77 @end
YYWaterflowView.h文件
1 //
2 // YYWaterflowView.h
3 // 06-瀑布流
4 //
5 // Created by apple on 14-7-29.
6 // Copyright (c) 2014年 wendingding. All rights reserved.
7 //
8
9 #import <UIKit/UIKit.h>
10
11 //使用瀑布流形式展示内容的控件
12 typedef enum {
13 YYWaterflowViewMarginTypeTop,
14 YYWaterflowViewMarginTypeBottom,
15 YYWaterflowViewMarginTypeLeft,
16 YYWaterflowViewMarginTypeRight,
17 YYWaterflowViewMarginTypeColumn,//每一列
18 YYWaterflowViewMarginTypeRow,//每一行
19
20 }YYWaterflowViewMarginType;
21
22 @class YYWaterflowViewCell,YYWaterflowView;
23
24 /**
25 * 1.数据源方法
26 */
27 @protocol YYWaterflowViewDataSource <NSObject>
28 //要求强制实现
29 @required
30 /**
31 * (1)一共有多少个数据
32 */
33 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView;
34 /**
35 * (2)返回index位置对应的cell
36 */
37 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;
38
39 //不要求强制实现
40 @optional
41 /**
42 * (3)一共有多少列
43 */
44 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView;
45
46 @end
47
48
49 /**
50 * 2.代理方法
51 */
52 @protocol YYWaterflowViewDelegate <UIScrollViewDelegate>
53 //不要求强制实现
54 @optional
55 /**
56 * (1)第index位置cell对应的高度
57 */
58 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;
59 /**
60 * (2)选中第index位置的cell
61 */
62 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;
63 /**
64 * (3)返回间距
65 */
66 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type;
67 @end
68
69
70 /**
71 * 3.瀑布流控件
72 */
73 @interface YYWaterflowView : UIScrollView
74 /**
75 * (1)数据源
76 */
77 @property(nonatomic,weak)id<YYWaterflowViewDataSource> dadaSource;
78 /**
79 * (2)代理
80 */
81 @property(nonatomic,weak)id<YYWaterflowViewDelegate> delegate;
82
83 /**
84 * 刷新数据
85 */
86 -(void)reloadData;
87 @end
瀑布流的内部实现(计算每个cell的frame)
YYWaterflowView.m文件的代码
1 //
2 // YYWaterflowView.m
3 // 06-瀑布流
4 //
5 // Created by apple on 14-7-29.
6 // Copyright (c) 2014年 wendingding. All rights reserved.
7 //
8
9 #import "YYWaterflowView.h"
10 #import "YYWaterflowViewCell.h"
11 #define YYWaterflowViewDefaultNumberOfClunms 3
12 #define YYWaterflowViewDefaultCellH 100
13 #define YYWaterflowViewDefaultMargin 10
14
15 @interface YYWaterflowView()
16 @property(nonatomic,strong)NSMutableArray *cellFrames;
17 @end
18
19 @implementation YYWaterflowView
20
21 #pragma mark-懒加载
22 -(NSMutableArray *)cellFrames
23 {
24 if (_cellFrames==nil) {
25 _cellFrames=[NSMutableArray array];
26 }
27 return _cellFrames;
28 }
29
30 - (id)initWithFrame:(CGRect)frame
31 {
32 self = [super initWithFrame:frame];
33 if (self) {
34 }
35 return self;
36 }
37
38 /**
39 * 刷新数据
40 * 1.计算每个cell的frame
41 */
42 -(void)reloadData
43 {
44 //cell的总数是多少
45 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self];
46
47 //cell的列数
48 int numberOfColumns=[self numberOfColumns];
49
50 //间距
51 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft];
52 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight];
53 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn];
54 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop];
55 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow];
56 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom];
57
58 //(1)cell的宽度
59 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数
60 CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns;
61
62
63
64 //用一个C语言的数组来存放所有列的最大的Y值
65 CGFloat maxYOfColumns[numberOfColumns];
66 for (int i=0; i<numberOfColumns; i++) {
67 //初始化数组的数值全部为0
68 maxYOfColumns[i]=0.0;
69 }
70
71
72 //计算每个cell的fram
73 for (int i=0; i<numberOfCells; i++) {
74
75 //(2)cell的高度
76 //询问代理i位置的高度
77 CGFloat cellH=[self heightAtIndex:i];
78
79 //cell处在第几列(最短的一列)
80 NSUInteger cellAtColumn=0;
81
82 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值)
83 //默认设置最短的一列为第一列(优化性能)
84 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn];
85
86 //求出最短的那一列
87 for (int j=0; j<numberOfColumns; j++) {
88 if (maxYOfColumns[j]<maxYOfCellAtColumn) {
89 cellAtColumn=j;
90 maxYOfCellAtColumn=maxYOfColumns[j];
91 }
92 }
93
94 //(3)cell的位置(X,Y)
95 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距)
96 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM);
97 //cell的Y,先设定为0
98 CGFloat cellY=0;
99 if (maxYOfCellAtColumn==0.0) {//首行
100 cellY=topM;
101 }else
102 {
103 cellY=maxYOfCellAtColumn+rowM;
104 }
105
106 //(4)设置cell的frame并添加到数组中
107 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH);
108 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
109
110 //更新最短那一列的最大的Y值
111 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame);
112
113 //显示cell
114 YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];
115 cell.frame=cellFrame;
116 [self addSubview:cell];
117 }
118
119 //设置contentSize
120 CGFloat contentH=maxYOfColumns[0];
121 for (int i=1; i<numberOfColumns; i++) {
122 if (maxYOfColumns[i]>contentH) {
123 contentH=maxYOfColumns[i];
124 }
125 }
126 contentH += bottomM;
127 self.contentSize=CGSizeMake(0, contentH);
128 }
129
130 #pragma mark-私有方法
131 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type
132 {
133 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {
134 return [self.delegate waterflowView:self marginForType:type];
135 }else
136 {
137 return YYWaterflowViewDefaultMargin;
138 }
139 }
140
141 -(NSUInteger)numberOfColumns
142 {
143 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {
144 return [self.dadaSource numberOfColumnsInWaterflowView:self];
145 }else
146 {
147 return YYWaterflowViewDefaultNumberOfClunms;
148 }
149 }
150
151 -(CGFloat)heightAtIndex:(NSUInteger)index
152 {
153 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {
154 return [self.delegate waterflowView:self heightAtIndex:index];
155 }else
156 {
157 return YYWaterflowViewDefaultCellH;
158 }
159 }
160 @end
实现的瀑布流效果:

2.简单说明
说明:
(1)瀑布流每一个的宽度是一样的,都是高度不一样
(2)补齐算法,哪里比较短就补哪里,不是简单的从左到右排(两列之间的差距越来越大)。

这就要求我们时刻知道每一列最大的Y值是多少,以比较哪里“最短”。
可以考虑使用一个C语言的数组来存放所有列的最大Y值
注意数组的初始化操作。

提示:瀑布流的最后一行一般都是参差不齐的。
可扩展性:
简单的修改cell的列数,即可修改布局。
(1)设置瀑布流为2列。


(2)设置瀑布流的列数为4列


(3)如果不设置列数,那么显示的列数默认为3列。
(4)如果不设置高度,那么显示的cell的高度为默认的高度,都是一样的。


(5)cell的上下左右,行和列之间的间距也可以进行调整,这里不做演示。
(6)在cell中可以添加自定义的控件,如Button、imageView等,此时可以向使用UITableView和UITableViewcell一样来使用YYWaterflowView和YYWaterflowViewCell。


3.存在的问题
上面的代码对cell的处理存在很大的性能问题,如果程序中又2000个cell,那么这里就创建了两千个cell,性能很差。
可以通过在layoutSubviews方法中打印查看。

说明:之所以为2002,是因为创建了2000个cell+2个滚动条(水平方向上的滚动条被隐藏了,但是仍然存在)
优化思路:放入到缓存池。
http://www.cnblogs.com/wendingding/p/3876039.html

浙公网安备 33010602011771号