首页 > 代码库 > iOS—自定义瀑布流控件
iOS—自定义瀑布流控件
一、简单说明
使用数据刷新框架:
该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用。
问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中)。
移除正在显示的cell:
(1)把字典中的所有的值,都从屏幕上移除
(2)清除字典中的所有元素
(3)清除cell的frame,每个位置的cell的frame都要重新计算
(4)清除可复用的缓存池。
该部分的代码如下:
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 -(void)willMoveToSuperview:(UIView *)newSuperview 66 { 67 [self reloadData]; 68 } 69 70 #pragma mark-公共方法 71 /** 72 * cell的宽度 73 */ 74 -(CGFloat)cellWidth 75 { 76 //cell的列数 77 int numberOfColumns=[self numberOfColumns]; 78 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 79 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight]; 80 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 81 return (self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 82 } 83 84 /** 85 * 刷新数据 86 * 1.计算每个cell的frame 87 */ 88 -(void)reloadData 89 { 90 /* 91 (1)把字典中的所有的值,都从屏幕上移除 92 (2)清除字典中的所有元素 93 (3)清除cell的frame,每个位置的cell的frame都要重新计算 94 (4)清除可复用的缓存池。 95 */ 96 97 [self.displayingCells.allValues makeObjectsPerformSelector:@selector(removeFromSuperview)]; 98 [self.displayingCells removeAllObjects]; 99 [self.cellFrames removeAllObjects];100 [self.reusableCells removeAllObjects];101 102 //cell的总数是多少103 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self];104 105 //cell的列数106 int numberOfColumns=[self numberOfColumns];107 108 //间距109 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft];110 111 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn];112 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop];113 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow];114 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom];115 116 //(1)cell的宽度117 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数118 // CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns;119 CGFloat cellW=[self cellWidth];120 121 //用一个C语言的数组来存放所有列的最大的Y值122 CGFloat maxYOfColumns[numberOfColumns];123 for (int i=0; i<numberOfColumns; i++) {124 //初始化数组的数值全部为0125 maxYOfColumns[i]=0.0;126 }127 128 129 //计算每个cell的fram130 for (int i=0; i<numberOfCells; i++) {131 132 //(2)cell的高度133 //询问代理i位置的高度134 CGFloat cellH=[self heightAtIndex:i];135 136 //cell处在第几列(最短的一列)137 NSUInteger cellAtColumn=0;138 139 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值)140 //默认设置最短的一列为第一列(优化性能)141 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn];142 143 //求出最短的那一列144 for (int j=0; j<numberOfColumns; j++) {145 if (maxYOfColumns[j]<maxYOfCellAtColumn) {146 cellAtColumn=j;147 maxYOfCellAtColumn=maxYOfColumns[j];148 }149 }150 151 //(3)cell的位置(X,Y)152 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距)153 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM);154 //cell的Y,先设定为0155 CGFloat cellY=0;156 if (maxYOfCellAtColumn==0.0) {//首行157 cellY=topM;158 }else159 {160 cellY=maxYOfCellAtColumn+rowM;161 }162 163 //设置cell的frame并添加到数组中164 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH);165 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];166 167 //更新最短那一列的最大的Y值168 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame);169 }170 171 //设置contentSize172 CGFloat contentH=maxYOfColumns[0];173 for (int i=1; i<numberOfColumns; i++) {174 if (maxYOfColumns[i]>contentH) {175 contentH=maxYOfColumns[i];176 }177 }178 contentH += bottomM;179 self.contentSize=CGSizeMake(0, contentH);180 }181 182 /**183 * 当UIScrollView滚动的时候也会调用这个方法184 */185 -(void)layoutSubviews186 {187 [super layoutSubviews];188 189 190 //向数据源索要对应位置的cell191 NSUInteger numberOfCells=self.cellFrames.count;192 for (int i=0; i<numberOfCells; i++) {193 //取出i位置的frame,注意转换194 CGRect cellFrame=[self.cellFrames[i] CGRectValue];195 196 //优先从字典中取出i位置的cell197 YYWaterflowViewCell *cell=self.displayingCells[@(i)];198 199 //判断i位置对应的frame在不在屏幕上(能否看见)200 if ([self isInScreen:cellFrame]) {//在屏幕上201 if (cell==nil) {202 cell= [self.dadaSource waterflowView:self cellAtIndex:i];203 cell.frame=cellFrame;204 [self addSubview:cell];205 206 //存放在字典中207 self.displayingCells[@(i)]=cell;208 }209 210 }else //不在屏幕上211 {212 if (cell) {213 //从scrollView和字典中删除214 [cell removeFromSuperview];215 [self.displayingCells removeObjectForKey:@(i)];216 217 //存放进缓存池218 [self.reusableCells addObject:cell];219 }220 }221 }222 // NSLog(@"%d",self.subviews.count);223 }224 225 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier226 {227 __block YYWaterflowViewCell *reusableCell=nil;228 [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) {229 if ([cell.identifier isEqualToString:identifier]) {230 reusableCell=cell;231 *stop=YES;232 }233 }];234 235 if (reusableCell) {//从缓存池中移除(已经用掉了)236 [self.reusableCells removeObject:reusableCell];237 }238 return reusableCell;239 }240 241 #pragma mark cell的事件处理242 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event243 {244 //如果没有点击事件的代理方法,那么就直接返回245 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)])246 return;247 248 //获得手指在屏幕上点击的触摸点249 UITouch *touch=[touches anyObject];250 CGPoint point1=[touch locationInView:touch.view];251 CGPoint point=[touch locationInView:self];252 NSLog(@"%@--%@",NSStringFromCGPoint(point),NSStringFromCGPoint(point1));253 254 __block NSNumber *selectIndex=nil;255 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, YYWaterflowViewCell *cell, BOOL *stop) {256 if (CGRectContainsPoint(cell.frame, point)) {257 selectIndex=key;258 *stop=YES;259 }260 }];261 if (selectIndex) {262 //需要转换263 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue];264 }265 266 }267 #pragma mark-私有方法268 /**269 * 判断一个人cell的frame有没有显示在屏幕上270 */271 -(BOOL)isInScreen:(CGRect)frame272 {273 // return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height);274 return (CGRectGetMaxY(frame) > self.contentOffset.y) &&275 (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height);276 277 }278 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type279 {280 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {281 return [self.delegate waterflowView:self marginForType:type];282 }else283 {284 return YYWaterflowViewDefaultMargin;285 }286 }287 288 -(NSUInteger)numberOfColumns289 {290 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {291 return [self.dadaSource numberOfColumnsInWaterflowView:self];292 }else293 {294 return YYWaterflowViewDefaultNumberOfClunms;295 }296 }297 298 -(CGFloat)heightAtIndex:(NSUInteger)index299 {300 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {301 return [self.delegate waterflowView:self heightAtIndex:index];302 }else303 {304 return YYWaterflowViewDefaultCellH;305 }306 }307 @end
二、刷新操作
刷新操作的代码设计:
1 // 2 // YYShopViewController.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-31. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYShopViewController.h" 10 #import "YYWaterflowView.h" 11 #import "YYWaterflowViewCell.h" 12 #import "YYShop.h" 13 #import "YYShopCell.h" 14 #import "MJExtension.h" 15 #import "MJRefresh.h" 16 17 @interface YYShopViewController ()<YYWaterflowViewDataSource,YYWaterflowViewDelegate> 18 @property(nonatomic,strong)NSMutableArray *shops; 19 @property(nonatomic,strong)YYWaterflowView *waterflowView; 20 @end 21 22 @implementation YYShopViewController 23 24 #pragma mark-懒加载 25 -(NSMutableArray *)shops 26 { 27 if (_shops==nil) { 28 _shops=[NSMutableArray array]; 29 } 30 return _shops; 31 } 32 - (void)viewDidLoad 33 { 34 [super viewDidLoad]; 35 36 //1.初始化数据 37 NSArray *newShop=[YYShop objectArrayWithFilename:@"2.plist"]; 38 [self.shops addObjectsFromArray:newShop]; 39 40 //2.创建一个瀑布流 41 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init]; 42 waterflow.autoresizingMask=UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; 43 waterflow.frame=self.view.bounds; 44 waterflow.delegate=self; 45 waterflow.dadaSource=self; 46 [self.view addSubview:waterflow]; 47 48 self.waterflowView=waterflow; 49 50 //3.实现数据的刷新 51 // [waterflow addFooterWithCallback:^{ 52 // NSLog(@"上拉数据刷新"); 53 // }]; 54 // 55 // [waterflow addHeaderWithCallback:^{ 56 // NSLog(@"下拉数据刷新"); 57 // }]; 58 59 [waterflow addHeaderWithTarget:self action:@selector(loadNewShops)]; 60 [waterflow addFooterWithTarget:self action:@selector(loadMoreShops)]; 61 } 62 63 -(void)loadNewShops 64 { 65 //模拟,只执行一次刷新操作 66 static dispatch_once_t onceToken; 67 dispatch_once(&onceToken, ^{ 68 //加载1.plist文件 69 NSArray *newShop=[YYShop objectArrayWithFilename:@"1.plist"]; 70 [self.shops insertObjects:newShop atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newShop.count)]]; 71 }); 72 73 //模拟网络延迟,2.0秒钟之后执行 74 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 75 //刷新数据 76 [self.waterflowView reloadData]; 77 78 //停止刷新 79 [self.waterflowView headerEndRefreshing]; 80 }); 81 } 82 83 -(void)loadMoreShops 84 { 85 static dispatch_once_t onceToken; 86 dispatch_once(&onceToken, ^{ 87 //加载1.plist文件 88 NSArray *newShop=[YYShop objectArrayWithFilename:@"3.plist"]; 89 [self.shops addObjectsFromArray:newShop]; 90 }); 91 92 //模拟网络延迟,2.0秒钟之后执行 93 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 94 //刷新数据 95 [self.waterflowView reloadData]; 96 97 //停止刷新 98 [self.waterflowView footerEndRefreshing]; 99 });100 101 }102 103 #pragma mark-数据源方法104 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView105 {106 return self.shops.count;107 }108 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView109 {110 return 3;111 }112 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index113 {114 YYShopCell *cell=[YYShopCell cellWithwaterflowView:waterflowView];115 cell.shop=self.shops[index];116 return cell;117 }118 119 120 #pragma mark-代理方法121 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index122 {123 YYShop *shop=self.shops[index];124 //根据Cell的宽度和图片的宽高比 算出cell的高度125 return waterflowView.cellWidth*shop.h/shop.w;126 }127 128 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index129 {130 NSLog(@"点击了第%d个cell",index);131 }132 133 134 @end
实现的刷新效果:
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。