iOS开发UI篇—自定义瀑布流控件(cell的循环利用)
iOS开发UI篇—自定义瀑布流控件(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 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的创建数量:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。