iOS开发UI篇—自定义瀑布流控件(cell的循环利用)

iOS开发UI篇—自定义瀑布流控件(cell的循环利用)

一、简单说明

  当滚动的时候,向数据源要cell。

  当UIScrollView滚动的时候会调用layoutSubviews在tableView中也是一样的,因此,可以用这个方法来监听scrollView的滚动,可以在在这个地方向数据源索要对应位置的cell(frame在屏幕上的cell)。
示例:
  当scrollView在屏幕上滚动的时候,离开屏幕的cell应该放到缓存池中去,询问即将(已经)进入到屏幕的cell,对于还没有进入到屏幕的cell不作处理。
 
判断cell有没有在屏幕上?
  cell的最大的Y值>contentoffset的y值,并且小于contentoffset的y值+UIView的高度

 代码示例:

 1 /**
 2  *  当UIScrollView滚动的时候也会调用这个方法
 3  */
 4 -(void)layoutSubviews
 5 {
 6     [super layoutSubviews];
 7     NSLog(@"%d",self.subviews.count);
 8     
 9     //向数据源索要对应位置的cell
10     NSUInteger numberOfCells=self.cellFrames.count;
11     for (int i=0; i<numberOfCells; i++) {
12         //取出i位置的frame,注意转换
13         CGRect cellFrame=[self.cellFrames[i] CGRectValue];
14         
15         //判断i位置对应的frame在不在屏幕上(能否看见)
16         if ([self isInScreen:cellFrame]) {//在屏幕上
17             YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];
18             cell.frame=cellFrame;
19             [self addSubview:cell];
20         }else //不在屏幕上
21         {
22             
23         }
24     }
25 }
26 #pragma mark-私有方法
27 /**
28  *  判断一个人cell的frame有没有显示在屏幕上
29  */
30 -(BOOL)isInScreen:(CGRect)frame
31 {
32     return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height);
33 }

上述代码存在一个容易忽视的问题,就是当用户在短距离之内来回拖动cell的时候,cell依然会创建新的cell并切换。

解决这个问题,可以考虑添加一个字典属性,把位置(i)和这个位置的cell存入到字典中,在创建cell(向数据源要数据)之前进行判断,如果该位置的cell存在,那么就不创建。

修正后的代码如下:

 1 /**
 2  *  当UIScrollView滚动的时候也会调用这个方法
 3  */
 4 -(void)layoutSubviews
 5 {
 6     [super layoutSubviews];
 7     NSLog(@"%d",self.subviews.count);
 8     
 9     //向数据源索要对应位置的cell
10     NSUInteger numberOfCells=self.cellFrames.count;
11     for (int i=0; i<numberOfCells; i++) {
12         //取出i位置的frame,注意转换
13         CGRect cellFrame=[self.cellFrames[i] CGRectValue];
14         
15         //判断i位置对应的frame在不在屏幕上(能否看见)
16         if ([self isInScreen:cellFrame]) {//在屏幕上
17           
18             //优先从字典中取出i位置的cell
19             YYWaterflowViewCell *cell=self.displayingCells[@(i)];
20             if (cell==nil) {
21                cell= [self.dadaSource waterflowView:self cellAtIndex:i];
22                 cell.frame=cellFrame;
23                 [self addSubview:cell];
24                 
25                 //存放在字典中
26                 self.displayingCells[@(i)]=cell;
27             }
28             
29         }else //不在屏幕上
30         {
31             
32         }
33     }
34 }

 

二、cell的循环利用

说明:使用set集合实现一个缓存池,当cell离开显示界面的时候,就把这个cell放到缓存池中,当下次使用的时候,直接去缓存池中取。

注意:放到缓存池中的cell是给控制器用的。

需要提供一个方法,仿照tableView根据标识去缓存池中查找可以循环利用的cell

   实现代码:

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 #pragma mark-公共方法
84 /**
85  *  刷新数据
86  */
87 -(void)reloadData;
88 /**
89  *  根据标识去缓存池中查找可循环利用的cell
90  */
91 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
92 @end

核心代码:

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 /**
 66  *  刷新数据
 67  *  1.计算每个cell的frame
 68  */
 69 -(void)reloadData
 70 {
 71     //cell的总数是多少
 72     int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self];
 73     
 74     //cell的列数
 75     int numberOfColumns=[self numberOfColumns];
 76     
 77     //间距
 78     CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft];
 79     CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight];
 80     CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn];
 81     CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop];
 82     CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow];
 83     CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom];
 84     
 85     //(1)cell的宽度
 86     //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数
 87     CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns;
 88 
 89     
 90     
 91     //用一个C语言的数组来存放所有列的最大的Y值
 92     CGFloat maxYOfColumns[numberOfColumns];
 93     for (int i=0; i<numberOfColumns; i++) {
 94         //初始化数组的数值全部为0
 95         maxYOfColumns[i]=0.0;
 96     }
 97     
 98     
 99     //计算每个cell的fram
100     for (int i=0; i<numberOfCells; i++) {
101         
102         //(2)cell的高度
103         //询问代理i位置的高度
104         CGFloat cellH=[self heightAtIndex:i];
105         
106         //cell处在第几列(最短的一列)
107         NSUInteger cellAtColumn=0;
108         
109         //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值)
110         //默认设置最短的一列为第一列(优化性能)
111         CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn];
112         
113         //求出最短的那一列
114         for (int j=0; j<numberOfColumns; j++) {
115             if (maxYOfColumns[j]<maxYOfCellAtColumn) {
116                 cellAtColumn=j;
117                 maxYOfCellAtColumn=maxYOfColumns[j];
118             }
119         }
120         
121         //(3)cell的位置(X,Y)
122         //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距)
123         CGFloat cellX=leftM+cellAtColumn*(cellW +columnM);
124         //cell的Y,先设定为0
125         CGFloat cellY=0;
126         if (maxYOfCellAtColumn==0.0) {//首行
127             cellY=topM;
128         }else
129         {
130             cellY=maxYOfCellAtColumn+rowM;
131         }
132         
133         //设置cell的frame并添加到数组中
134         CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH);
135         [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
136         
137         //更新最短那一列的最大的Y值
138         maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame);
139         
140         //显示cell
141 //        YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];
142 //        cell.frame=cellFrame;
143 //        [self addSubview:cell];
144     }
145     
146     //设置contentSize
147     CGFloat contentH=maxYOfColumns[0];
148     for (int i=1; i<numberOfColumns; i++) {
149         if (maxYOfColumns[i]>contentH) {
150             contentH=maxYOfColumns[i];
151         }
152     }
153     contentH += bottomM;
154     self.contentSize=CGSizeMake(0, contentH);
155 }
156 
157 /**
158  *  当UIScrollView滚动的时候也会调用这个方法
159  */
160 -(void)layoutSubviews
161 {
162     [super layoutSubviews];
163  
164     
165     //向数据源索要对应位置的cell
166     NSUInteger numberOfCells=self.cellFrames.count;
167     for (int i=0; i<numberOfCells; i++) {
168         //取出i位置的frame,注意转换
169         CGRect cellFrame=[self.cellFrames[i] CGRectValue];
170         
171         //优先从字典中取出i位置的cell
172         YYWaterflowViewCell *cell=self.displayingCells[@(i)];
173         
174         //判断i位置对应的frame在不在屏幕上(能否看见)
175         if ([self isInScreen:cellFrame]) {//在屏幕上
176             if (cell==nil) {
177                cell= [self.dadaSource waterflowView:self cellAtIndex:i];
178                 cell.frame=cellFrame;
179                 [self addSubview:cell];
180                 
181                 //存放在字典中
182                 self.displayingCells[@(i)]=cell;
183             }
184             
185         }else //不在屏幕上
186         {
187             if (cell) {
188                 //从scrollView和字典中删除
189                 [cell removeFromSuperview];
190                 [self.displayingCells removeObjectForKey:@(i)];
191                 
192                 //存放进缓存池
193                 [self.reusableCells addObject:cell];
194             }
195         }
196     }
197        NSLog(@"%d",self.subviews.count);
198 }
199 
200 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier
201 {
202    __block YYWaterflowViewCell *reusableCell=nil;
203     [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) {
204         if ([cell.identifier isEqualToString:identifier]) {
205             reusableCell=cell;
206             *stop=YES;
207         }
208     }];
209     
210     if (reusableCell) {//从缓存池中移除(已经用掉了)
211         [self.reusableCells removeObject:reusableCell];
212     }
213     return reusableCell;
214 }
215 
216 #pragma mark-私有方法
217 /**
218  *  判断一个人cell的frame有没有显示在屏幕上
219  */
220 -(BOOL)isInScreen:(CGRect)frame
221 {
222 //    return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height);
223     return (CGRectGetMaxY(frame) > self.contentOffset.y) &&
224     (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height);
225 
226 }
227 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type
228 {
229     if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {
230        return  [self.delegate waterflowView:self marginForType:type];
231     }else
232     {
233         return YYWaterflowViewDefaultMargin;
234     }
235 }
236 
237 -(NSUInteger)numberOfColumns
238 {
239     if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {
240         return [self.dadaSource numberOfColumnsInWaterflowView:self];
241     }else
242     {
243         return  YYWaterflowViewDefaultNumberOfClunms;
244     }
245 }
246 
247 -(CGFloat)heightAtIndex:(NSUInteger)index
248 {
249     if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {
250         return [self.delegate waterflowView:self heightAtIndex:index];
251     }else
252     {
253         return YYWaterflowViewDefaultCellH;
254     }
255 }
256 @end

 YYWaterflowViewCell.h文件

 1 //
 2 //  YYWaterflowViewCell.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 @interface YYWaterflowViewCell : UIView
12 @property(nonatomic,copy)NSString *identifier;
13 @end

控制器中cell的处理

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 40;
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 
45     static NSString *ID=@"cell";
46     YYWaterflowViewCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID];
47     if (cell==nil) {
48         cell=[[YYWaterflowViewCell alloc]init];
49         cell.identifier=ID;
50         //给cell设置一个随机色
51         cell.backgroundColor=YYRandomColor;
52         [cell addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]];
53     }
54  
55     return cell;
56 }
57 
58 
59 #pragma mark-代理方法
60 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index
61 {
62     switch (index%3) {
63         case 0:return 90;
64         case 1:return 110;
65         case 2:return 80;
66         default:return 120;
67     }
68 }
69 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type
70 {
71     switch (type) {
72         case YYWaterflowViewMarginTypeTop:
73         case YYWaterflowViewMarginTypeBottom:
74         case YYWaterflowViewMarginTypeLeft:
75         case YYWaterflowViewMarginTypeRight:
76             return 10;
77         case YYWaterflowViewMarginTypeColumn:
78         case YYWaterflowViewMarginTypeRow:
79             return 5;
80     }
81 }
82 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index
83 {
84     NSLog(@"点击了%d的cell",index);
85 }
86 @end

实现效果:

打印查看Cell的创建数量:

iOS开发UI篇—自定义瀑布流控件(cell的循环利用),,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。