iOS開發UI篇—自定義瀑布流控件(cell的事件處理)
一、關于cell的複用的補充
在設定每個索引位置對應的cell的方法中,列印cell的索引和位址,已檢視cell的循環利用情況
1 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index
2 {
3 // YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init];
4
5 static NSString *ID=@"cell";
6 YYWaterflowViewCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID];
7 if (cell==nil) {
8 cell=[[YYWaterflowViewCell alloc]init];
9 cell.identifier=ID;
10 //給cell設定一個随機色
11 cell.backgroundColor=YYRandomColor;
12 // [cell addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]];
13 }
14 //通過取出cell中的label,重新為label的text指派
15
16 NSLog(@"%d---%p",index,cell);
17 return cell;
18 }
檢視:
二、事件處理
1.在cell建立的時候添加一個label,設定label的tag值,以便在之後能夠擷取到label。
代碼示例:
YYViewController.m檔案
1 // YYViewController.m
2 // 06-瀑布流
3 //
4 // Created by apple on 14-7-28.
5 // Copyright (c) 2014年 wendingding. All rights reserved.
6 //
7
8 #import "YYViewController.h"
9 #import "YYWaterflowView.h"
10 #import "YYWaterflowViewCell.h"
11
12 @interface YYViewController ()<YYWaterflowViewDelegate,YYWaterflowViewDataSource>
13
14 @end
15
16 @implementation YYViewController
17
18 - (void)viewDidLoad
19 {
20 [super viewDidLoad];
21 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init];
22 waterflow.frame=self.view.bounds;
23 waterflow.delegate=self;
24 waterflow.dadaSource=self;
25 [self.view addSubview:waterflow];
26
27 //重新整理資料
28 [waterflow reloadData];
29 }
30
31 #pragma mark-資料源方法
32 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView
33 {
34 return 40;
35 }
36 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView
37 {
38 return 3;
39 }
40 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index
41 {
42 // YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init];
43
44 static NSString *ID=@"cell";
45 YYWaterflowViewCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID];
46 if (cell==nil) {
47 cell=[[YYWaterflowViewCell alloc]init];
48 cell.identifier=ID;
49 //給cell設定一個随機色
50 cell.backgroundColor=YYRandomColor;
51 // [cell addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]];
52 //在cell中添加一個label,設定tag值
53 UILabel *label=[[UILabel alloc]init];
54 label.tag=10;
55 //注意:需要設定控件的frame值,否則不會顯示
56 label.frame=CGRectMake(0, 0, 20, 20);
57 [cell addSubview:label];
58 }
59 //通過取出cell中的label,重新為label的text指派
60 UILabel *label=(UILabel *)[cell viewWithTag:10];
61 label.text=[NSString stringWithFormat:@"%d",index];
62
63 NSLog(@"%d---%p",index,cell);
64 return cell;
65 }
66
67
68 #pragma mark-代理方法
69 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index
70 {
71 switch (index%3) {
72 case 0:return 90;
73 case 1:return 110;
74 case 2:return 80;
75 default:return 120;
76 }
77 }
78 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type
79 {
80 switch (type) {
81 case YYWaterflowViewMarginTypeTop:
82 case YYWaterflowViewMarginTypeBottom:
83 case YYWaterflowViewMarginTypeLeft:
84 case YYWaterflowViewMarginTypeRight:
85 return 10;
86 case YYWaterflowViewMarginTypeColumn:
87 case YYWaterflowViewMarginTypeRow:
88 return 5;
89 }
90 }
91 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index
92 {
93 NSLog(@"點選了第%d個cell",index);
94 }
95 @end
2.在YYWaterflowView.m中,監聽手指對瀑布流的觸碰,獲得手指在螢幕上點選的觸摸點,判斷該觸摸點是否在cell上,在哪個cell上?
如果點選了cell調用代理方法,那麼處理對應cell的點選事件。
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 /**
17 * 所有cell的frame資料
18 */
19 @property(nonatomic,strong)NSMutableArray *cellFrames;
20 /**
21 * 正在展示的cell
22 */
23 @property(nonatomic,strong)NSMutableDictionary *displayingCells;
24 /**
25 * 緩存池(使用SET)
26 */
27 @property(nonatomic,strong)NSMutableSet *reusableCells;
28 @end
29
30 @implementation YYWaterflowView
31
32 #pragma mark-懶加載
33 -(NSMutableArray *)cellFrames
34 {
35 if (_cellFrames==nil) {
36 _cellFrames=[NSMutableArray array];
37 }
38 return _cellFrames;
39 }
40
41 -(NSMutableDictionary *)displayingCells
42 {
43 if (_displayingCells==nil) {
44 _displayingCells=[NSMutableDictionary dictionary];
45 }
46 return _displayingCells;
47 }
48
49 -(NSMutableSet *)reusableCells
50 {
51 if (_reusableCells==nil) {
52 _reusableCells=[NSMutableSet set];
53 }
54 return _reusableCells;
55 }
56
57 - (id)initWithFrame:(CGRect)frame
58 {
59 self = [super initWithFrame:frame];
60 if (self) {
61 }
62 return self;
63 }
64
65 #pragma mark-公共方法
66 /**
67 * 重新整理資料
68 * 1.計算每個cell的frame
69 */
70 -(void)reloadData
71 {
72 //cell的總數是多少
73 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self];
74
75 //cell的列數
76 int numberOfColumns=[self numberOfColumns];
77
78 //間距
79 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft];
80 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight];
81 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn];
82 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop];
83 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow];
84 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom];
85
86 //(1)cell的寬度
87 //cell的寬度=(整個view的寬度-左邊的間距-右邊的間距-(列數-1)X每列之間的間距)/總列數
88 CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns;
89
90
91
92 //用一個C語言的數組來存放所有列的最大的Y值
93 CGFloat maxYOfColumns[numberOfColumns];
94 for (int i=0; i<numberOfColumns; i++) {
95 //初始化數組的數值全部為0
96 maxYOfColumns[i]=0.0;
97 }
98
99
100 //計算每個cell的fram
101 for (int i=0; i<numberOfCells; i++) {
102
103 //(2)cell的高度
104 //詢問代理i位置的高度
105 CGFloat cellH=[self heightAtIndex:i];
106
107 //cell處在第幾列(最短的一列)
108 NSUInteger cellAtColumn=0;
109
110 //cell所處那列的最大的Y值(目前最短的那一列的最大的Y值)
111 //預設設定最短的一列為第一列(優化性能)
112 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn];
113
114 //求出最短的那一列
115 for (int j=0; j<numberOfColumns; j++) {
116 if (maxYOfColumns[j]<maxYOfCellAtColumn) {
117 cellAtColumn=j;
118 maxYOfCellAtColumn=maxYOfColumns[j];
119 }
120 }
121
122 //(3)cell的位置(X,Y)
123 //cell的X=左邊的間距+列号*(cell的寬度+每列之間的間距)
124 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM);
125 //cell的Y,先設定為0
126 CGFloat cellY=0;
127 if (maxYOfCellAtColumn==0.0) {//首行
128 cellY=topM;
129 }else
130 {
131 cellY=maxYOfCellAtColumn+rowM;
132 }
133
134 //設定cell的frame并添加到數組中
135 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH);
136 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
137
138 //更新最短那一列的最大的Y值
139 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame);
140
141 //顯示cell
142 // YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];
143 // cell.frame=cellFrame;
144 // [self addSubview:cell];
145 }
146
147 //設定contentSize
148 CGFloat contentH=maxYOfColumns[0];
149 for (int i=1; i<numberOfColumns; i++) {
150 if (maxYOfColumns[i]>contentH) {
151 contentH=maxYOfColumns[i];
152 }
153 }
154 contentH += bottomM;
155 self.contentSize=CGSizeMake(0, contentH);
156 }
157
158 /**
159 * 當UIScrollView滾動的時候也會調用這個方法
160 */
161 -(void)layoutSubviews
162 {
163 [super layoutSubviews];
164
165
166 //向資料源索要對應位置的cell
167 NSUInteger numberOfCells=self.cellFrames.count;
168 for (int i=0; i<numberOfCells; i++) {
169 //取出i位置的frame,注意轉換
170 CGRect cellFrame=[self.cellFrames[i] CGRectValue];
171
172 //優先從字典中取出i位置的cell
173 YYWaterflowViewCell *cell=self.displayingCells[@(i)];
174
175 //判斷i位置對應的frame在不在螢幕上(能否看見)
176 if ([self isInScreen:cellFrame]) {//在螢幕上
177 if (cell==nil) {
178 cell= [self.dadaSource waterflowView:self cellAtIndex:i];
179 cell.frame=cellFrame;
180 [self addSubview:cell];
181
182 //存放在字典中
183 self.displayingCells[@(i)]=cell;
184 }
185
186 }else //不在螢幕上
187 {
188 if (cell) {
189 //從scrollView和字典中删除
190 [cell removeFromSuperview];
191 [self.displayingCells removeObjectForKey:@(i)];
192
193 //存放進緩存池
194 [self.reusableCells addObject:cell];
195 }
196 }
197 }
198 // NSLog(@"%d",self.subviews.count);
199 }
200
201 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier
202 {
203 __block YYWaterflowViewCell *reusableCell=nil;
204 [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) {
205 if ([cell.identifier isEqualToString:identifier]) {
206 reusableCell=cell;
207 *stop=YES;
208 }
209 }];
210
211 if (reusableCell) {//從緩存池中移除(已經用掉了)
212 [self.reusableCells removeObject:reusableCell];
213 }
214 return reusableCell;
215 }
216
217 #pragma mark cell的事件處理
218 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
219 {
220 //如果沒有點選事件的代理方法,那麼就直接傳回
221 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)])
222 return;
223
224 //獲得手指在螢幕上點選的觸摸點
225 UITouch *touch=[touches anyObject];
226 // CGPoint point=[touch locationInView:touch.view];
227 CGPoint point=[touch locationInView:self];
228
229 __block NSNumber *selectIndex=nil;
230 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, YYWaterflowViewCell *cell, BOOL *stop) {
231 if (CGRectContainsPoint(cell.frame, point)) {
232 selectIndex=key;
233 *stop=YES;
234 }
235 }];
236 if (selectIndex) {
237 //需要轉換
238 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue];
239 }
240
241 }
242 #pragma mark-私有方法
243 /**
244 * 判斷一個人cell的frame有沒有顯示在螢幕上
245 */
246 -(BOOL)isInScreen:(CGRect)frame
247 {
248 // return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height);
249 return (CGRectGetMaxY(frame) > self.contentOffset.y) &&
250 (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height);
251
252 }
253 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type
254 {
255 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {
256 return [self.delegate waterflowView:self marginForType:type];
257 }else
258 {
259 return YYWaterflowViewDefaultMargin;
260 }
261 }
262
263 -(NSUInteger)numberOfColumns
264 {
265 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {
266 return [self.dadaSource numberOfColumnsInWaterflowView:self];
267 }else
268 {
269 return YYWaterflowViewDefaultNumberOfClunms;
270 }
271 }
272
273 -(CGFloat)heightAtIndex:(NSUInteger)index
274 {
275 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {
276 return [self.delegate waterflowView:self heightAtIndex:index];
277 }else
278 {
279 return YYWaterflowViewDefaultCellH;
280 }
281 }
282 @end
實作效果:
點選對應的cell,能夠監聽并對點選事件進行處理。
三、補充說明
示例代碼:
說明:
touch.view指的是子控件(單個cell)以這個cell的左上角為(0,0)進行坐标計算
self指的是整個瀑布流,以整個瀑布流的左上角為(0,0)進行坐标計算
列印檢視: