首页 > 代码库 > iOS开发UI篇—自定义瀑布流控件(cell的循环利用)
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 //向数据源索要对应位置的cell10 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)frame31 {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 //向数据源索要对应位置的cell10 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位置的cell19 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 @required30 /**31 * (1)一共有多少个数据32 */33 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView;34 /**35 * (2)返回index位置对应的cell36 */37 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;38 39 //不要求强制实现40 @optional41 /**42 * (3)一共有多少列43 */44 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView;45 46 @end47 48 49 /**50 * 2.代理方法51 */52 @protocol YYWaterflowViewDelegate <UIScrollViewDelegate>53 //不要求强制实现54 @optional55 /**56 * (1)第index位置cell对应的高度57 */58 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;59 /**60 * (2)选中第index位置的cell61 */62 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;63 /**64 * (3)返回间距65 */66 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type;67 @end68 69 70 /**71 * 3.瀑布流控件72 */73 @interface YYWaterflowView : UIScrollView74 /**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 * 根据标识去缓存池中查找可循环利用的cell90 */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的fram100 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,先设定为0125 CGFloat cellY=0;126 if (maxYOfCellAtColumn==0.0) {//首行127 cellY=topM;128 }else129 {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 //显示cell141 // YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];142 // cell.frame=cellFrame;143 // [self addSubview:cell];144 }145 146 //设置contentSize147 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)layoutSubviews161 {162 [super layoutSubviews];163 164 165 //向数据源索要对应位置的cell166 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位置的cell172 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 *)identifier201 {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)frame221 {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)type228 {229 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {230 return [self.delegate waterflowView:self marginForType:type];231 }else232 {233 return YYWaterflowViewDefaultMargin;234 }235 }236 237 -(NSUInteger)numberOfColumns238 {239 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {240 return [self.dadaSource numberOfColumnsInWaterflowView:self];241 }else242 {243 return YYWaterflowViewDefaultNumberOfClunms;244 }245 }246 247 -(CGFloat)heightAtIndex:(NSUInteger)index248 {249 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {250 return [self.delegate waterflowView:self heightAtIndex:index];251 }else252 {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 : UIView12 @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 @end16 17 @implementation YYViewController18 19 - (void)viewDidLoad20 {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 *)waterflowView34 {35 return 40;36 }37 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView38 {39 return 3;40 }41 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index42 {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)index61 {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)type70 {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)index83 {84 NSLog(@"点击了%d的cell",index);85 }86 @end
实现效果:
打印查看Cell的创建数量:
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。