转自:http://www.cnblogs.com/wendingding/p/3881485.html
一、简单说明
关于瀑布流
1.是使用UIScrollView实现的
2.刷新数据(reloadData)方法里面做哪些事情
3.layoutSubviews方法里面做哪些事情
4.模仿UItableView进行设计
完善:
瀑布流控件第一次显示到屏幕上的时候自动的向数据源索要数据,而不需要手动调用。这需要监听View的显示,View的显示有一个方法,叫做willMoveToSuperview:在该方法中直接刷新一次数据即可。
![](http://images.cnitblog.com/i/450136/201407/311735188839760.png)
二、把自定义的瀑布流控件作为一个框架进行使用
1.框架
把自定义的瀑布流控件转变成一个框架。以后可以把它作为一套框架进行开发。
![](http://images.cnitblog.com/i/450136/201407/311832221334872.png)
2.瀑布流框架的使用
在做瀑布流应用的时候,一定要和服务器开发人员沟通清楚,提供的数据一定要包括图片的宽度和高度,否则没有办法进行处理。
为了保证图片不会变形,因此不能直接返回图片的宽度或者是高度,而应该使用宽高的比值。应该根据cell的宽度,有图片的宽高比计算出图片的高度(正式的高度!=真实的高度 cellW/cellH=真实的宽度/真实的高度==>cellH=cellW*真实的高度/真实的宽度)。
需要在框架中提供一个接口,获取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 *cell的宽度 86 */ 87 -(CGFloat)cellWidth; 88 /** 89 * 刷新数据 90 */ 91 -(void)reloadData; 92 /** 93 * 根据标识去缓存池中查找可循环利用的cell 94 */ 95 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier; 96 @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 -(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 //cell的总数是多少 91 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self]; 92 93 //cell的列数 94 int numberOfColumns=[self numberOfColumns]; 95 96 //间距 97 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 98 99 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 100 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop]; 101 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow]; 102 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom]; 103 104 //(1)cell的宽度 105 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数 106 // CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 107 CGFloat cellW=[self cellWidth]; 108 109 //用一个C语言的数组来存放所有列的最大的Y值 110 CGFloat maxYOfColumns[numberOfColumns]; 111 for (int i=0; i<numberOfColumns; i++) { 112 //初始化数组的数值全部为0 113 maxYOfColumns[i]=0.0; 114 } 115 116 117 //计算每个cell的fram 118 for (int i=0; i<numberOfCells; i++) { 119 120 //(2)cell的高度 121 //询问代理i位置的高度 122 CGFloat cellH=[self heightAtIndex:i]; 123 124 //cell处在第几列(最短的一列) 125 NSUInteger cellAtColumn=0; 126 127 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值) 128 //默认设置最短的一列为第一列(优化性能) 129 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn]; 130 131 //求出最短的那一列 132 for (int j=0; j<numberOfColumns; j++) { 133 if (maxYOfColumns[j]<maxYOfCellAtColumn) { 134 cellAtColumn=j; 135 maxYOfCellAtColumn=maxYOfColumns[j]; 136 } 137 } 138 139 //(3)cell的位置(X,Y) 140 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距) 141 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM); 142 //cell的Y,先设定为0 143 CGFloat cellY=0; 144 if (maxYOfCellAtColumn==0.0) {//首行 145 cellY=topM; 146 }else 147 { 148 cellY=maxYOfCellAtColumn+rowM; 149 } 150 151 //设置cell的frame并添加到数组中 152 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH); 153 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]]; 154 155 //更新最短那一列的最大的Y值 156 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame); 157 158 //显示cell 159 // YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i]; 160 // cell.frame=cellFrame; 161 // [self addSubview:cell]; 162 } 163 164 //设置contentSize 165 CGFloat contentH=maxYOfColumns[0]; 166 for (int i=1; i<numberOfColumns; i++) { 167 if (maxYOfColumns[i]>contentH) { 168 contentH=maxYOfColumns[i]; 169 } 170 } 171 contentH += bottomM; 172 self.contentSize=CGSizeMake(0, contentH); 173 } 174 175 /** 176 * 当UIScrollView滚动的时候也会调用这个方法 177 */ 178 -(void)layoutSubviews 179 { 180 [super layoutSubviews]; 181 182 183 //向数据源索要对应位置的cell 184 NSUInteger numberOfCells=self.cellFrames.count; 185 for (int i=0; i<numberOfCells; i++) { 186 //取出i位置的frame,注意转换 187 CGRect cellFrame=[self.cellFrames[i] CGRectValue]; 188 189 //优先从字典中取出i位置的cell 190 YYWaterflowViewCell *cell=self.displayingCells[@(i)]; 191 192 //判断i位置对应的frame在不在屏幕上(能否看见) 193 if ([self isInScreen:cellFrame]) {//在屏幕上 194 if (cell==nil) { 195 cell= [self.dadaSource waterflowView:self cellAtIndex:i]; 196 cell.frame=cellFrame; 197 [self addSubview:cell]; 198 199 //存放在字典中 200 self.displayingCells[@(i)]=cell; 201 } 202 203 }else //不在屏幕上 204 { 205 if (cell) { 206 //从scrollView和字典中删除 207 [cell removeFromSuperview]; 208 [self.displayingCells removeObjectForKey:@(i)]; 209 210 //存放进缓存池 211 [self.reusableCells addObject:cell]; 212 } 213 } 214 } 215 // NSLog(@"%d",self.subviews.count); 216 } 217 218 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier 219 { 220 __block YYWaterflowViewCell *reusableCell=nil; 221 [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) { 222 if ([cell.identifier isEqualToString:identifier]) { 223 reusableCell=cell; 224 *stop=YES; 225 } 226 }]; 227 228 if (reusableCell) {//从缓存池中移除(已经用掉了) 229 [self.reusableCells removeObject:reusableCell]; 230 } 231 return reusableCell; 232 } 233 234 #pragma mark cell的事件处理 235 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 236 { 237 //如果没有点击事件的代理方法,那么就直接返回 238 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) 239 return; 240 241 //获得手指在屏幕上点击的触摸点 242 UITouch *touch=[touches anyObject]; 243 CGPoint point1=[touch locationInView:touch.view]; 244 CGPoint point=[touch locationInView:self]; 245 NSLog(@"%@--%@",NSStringFromCGPoint(point),NSStringFromCGPoint(point1)); 246 247 __block NSNumber *selectIndex=nil; 248 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, YYWaterflowViewCell *cell, BOOL *stop) { 249 if (CGRectContainsPoint(cell.frame, point)) { 250 selectIndex=key; 251 *stop=YES; 252 } 253 }]; 254 if (selectIndex) { 255 //需要转换 256 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue]; 257 } 258 259 } 260 #pragma mark-私有方法 261 /** 262 * 判断一个人cell的frame有没有显示在屏幕上 263 */ 264 -(BOOL)isInScreen:(CGRect)frame 265 { 266 // return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height); 267 return (CGRectGetMaxY(frame) > self.contentOffset.y) && 268 (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height); 269 270 } 271 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type 272 { 273 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) { 274 return [self.delegate waterflowView:self marginForType:type]; 275 }else 276 { 277 return YYWaterflowViewDefaultMargin; 278 } 279 } 280 281 -(NSUInteger)numberOfColumns 282 { 283 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) { 284 return [self.dadaSource numberOfColumnsInWaterflowView:self]; 285 }else 286 { 287 return YYWaterflowViewDefaultNumberOfClunms; 288 } 289 } 290 291 -(CGFloat)heightAtIndex:(NSUInteger)index 292 { 293 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) { 294 return [self.delegate waterflowView:self heightAtIndex:index]; 295 }else 296 { 297 return YYWaterflowViewDefaultCellH; 298 } 299 } 300 @end
提示:瀑布流有一个特点,即每个cell的宽度都是一样的。
三、蘑菇街的实现
1.新建一个项目,使用自定义的瀑布流框架
观察plist文件的数据结构
![](http://images.cnitblog.com/i/450136/201407/311838545081566.png)
新建一个shop模型,继承自NSObject类,
![](http://images.cnitblog.com/i/450136/201407/311837511809213.png)
该类中的代码设计如下:
YYShop.h文件
1 // 2 // YYShop.h 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-31. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface YYShop : NSObject 12 /** 13 * 图片的高度 14 */ 15 @property(nonatomic,assign)CGFloat h; 16 /** 17 * 图片的宽度 18 */ 19 @property(nonatomic,assign)CGFloat w; 20 /** 21 * 图片的网络地址 22 */ 23 @property(nonatomic,copy)NSString *img; 24 /** 25 * 商品的价格 26 */ 27 @property(nonatomic,copy)NSString *price; 28 @end
控制器中的代码设计和处理:
YYShopViewController.m文件
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 16 @interface YYShopViewController ()<YYWaterflowViewDataSource,YYWaterflowViewDelegate> 17 @property(nonatomic,strong)NSMutableArray *shops; 18 @end 19 20 @implementation YYShopViewController 21 22 #pragma mark-懒加载 23 -(NSMutableArray *)shops 24 { 25 if (_shops==nil) { 26 _shops=[NSMutableArray array]; 27 } 28 return _shops; 29 } 30 - (void)viewDidLoad 31 { 32 [super viewDidLoad]; 33 //1.初始化数据 34 NSArray *newShop=[YYShop objectArrayWithFilename:@"1.plist"]; 35 [self.shops addObjectsFromArray:newShop]; 36 37 //2.创建一个瀑布流 38 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init]; 39 waterflow.frame=self.view.bounds; 40 waterflow.delegate=self; 41 waterflow.dadaSource=self; 42 [self.view addSubview:waterflow]; 43 } 44 #pragma mark-数据源方法 45 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView 46 { 47 return 40; 48 } 49 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView 50 { 51 return 3; 52 } 53 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 54 { 55 YYShopCell *cell=[YYShopCell cellWithwaterflowView:waterflowView]; 56 cell.shop=self.shops[index]; 57 return cell; 58 } 59 60 61 #pragma mark-代理方法 62 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index 63 { 64 YYShop *shop=self.shops[index]; 65 //根据Cell的宽度和图片的宽高比 算出cell的高度 66 return waterflowView.cellWidth*shop.h/shop.w; 67 } 68 69 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index 70 { 71 NSLog(@"点击了第%d个cell",index); 72 } 73 74 75 @end
对瀑布流的cell进行自定义,按照需要的方式处理cell中的子控件(这里包括imageView和label控件)
自定义cell的代码处理如下:
YYShopCell.h文件
1 // 2 // YYShopCell.h 3 // 06-瀑布流 4 // Created by apple on 14-7-31. 5 // Copyright (c) 2014年 wendingding. All rights reserved. 6 // 7 8 #import "YYWaterflowViewCell.h" 9 10 @class YYWaterflowView,YYShop; 11 @interface YYShopCell : YYWaterflowViewCell 12 @property(nonatomic,strong)YYShop *shop; 13 +(instancetype)cellWithwaterflowView:(YYWaterflowView *)waterflowView; 14 @end
YYShopCell.m文件
1 // 2 // YYShopCell.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-31. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYShopCell.h" 10 #import "YYWaterflowView.h" 11 #import "YYWaterflowViewCell.h" 12 #import "YYShop.h" 13 #import "UIImageView+WebCache.h" 14 15 @interface YYShopCell () 16 @property(nonatomic,strong)UIImageView *imageView; 17 @property(nonatomic,strong)UILabel *priceLabel; 18 @end 19 @implementation YYShopCell 20 21 - (id)initWithFrame:(CGRect)frame 22 { 23 self = [super initWithFrame:frame]; 24 if (self) { 25 UIImageView *imageView=[[UIImageView alloc]init]; 26 [self addSubview:imageView]; 27 self.imageView=imageView; 28 29 UILabel *priceLabel=[[UILabel alloc]init]; 30 priceLabel.backgroundColor=[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]; 31 priceLabel.textAlignment=NSTextAlignmentCenter; 32 priceLabel.textColor=[UIColor whiteColor]; 33 [self addSubview:priceLabel]; 34 self.priceLabel=priceLabel; 35 36 } 37 return self; 38 } 39 40 +(instancetype)cellWithwaterflowView:(YYWaterflowView *)waterflowView 41 { 42 static NSString *ID=@"ID"; 43 YYShopCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID]; 44 if (cell==nil) { 45 cell=[[YYShopCell alloc]init]; 46 cell.identifier=ID; 47 } 48 return cell; 49 } 50 51 -(void)setShop:(YYShop *)shop 52 { 53 _shop=shop; 54 self.priceLabel.text=shop.price; 55 [self.imageView sd_setImageWithURL:[NSURL URLWithString:shop.img] placeholderImage:[UIImage imageNamed:@"loading"]]; 56 } 57 58 -(void)layoutSubviews 59 { 60 [super layoutSubviews]; 61 62 self.imageView.frame=self.bounds; 63 64 CGFloat priceX=0; 65 CGFloat priceH=25; 66 CGFloat priceY=self.bounds.size.height-priceH; 67 CGFloat priceW=self.bounds.size.width; 68 69 self.priceLabel.frame=CGRectMake(priceX, priceY, priceW, priceH); 70 } 71 @end
2.代码说明
该项目中使用了第三方框架如下:分别用来处理字典转模型,下载网络图片。
在pch文件中对随机色的处理代码:
1 // 2 // Prefix header 3 // 4 // The contents of this file are implicitly included at the beginning of every source file. 5 // 6 7 #import <Availability.h> 8 9 #ifndef __IPHONE_5_0 10 #warning "This project uses features only available in iOS SDK 5.0 and later." 11 #endif 12 13 #ifdef __OBJC__ 14 #import <UIKit/UIKit.h> 15 #import <Foundation/Foundation.h> 16 17 // 颜色 18 #define YYColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] 19 #define YYColorRGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a] 20 21 // 随机色 22 #define YYRandomColor YYColor(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256)) 23 #endif
说明:已经实现了cell的循环利用。