iOS开发UI篇—自定义瀑布流控件(接口设计)
一、简单说明
1.关于瀑布流
电商应用要展示商品信息通常是通过瀑布流的方式,因为每个商品的展示图片,长度和商都都不太一样。
如果不用瀑布流的话,展示这样的格子数据,还有一种办法是使用九宫格。
但利用九宫格有一个缺点,那就是每个格子的宽高是一样的,如果一定要使用九宫格来展示,那么展示的商品图片可能会变形。
为了保证商品图片能够按照原来的宽高比进行展示,一般采用的是瀑布流的方式。
1 // 2 // YYWaterflowView.h 3 // 06-瀑布流01接口设计 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 @end
主控制器中的使用:
YYViewController.m文件的代码
1 // 2 // YYViewController.m 3 // 06-瀑布流01接口设计 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 #pragma mark-数据源方法 30 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView 31 { 32 return 100; 33 } 34 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView 35 { 36 return 3; 37 } 38 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 39 { 40 YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init]; 41 //给cell设置一个随机色 42 cell.backgroundColor=YYRandomColor; 43 return cell; 44 } 45 46 47 #pragma mark-代理方法 48 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index 49 { 50 switch (index%3) { 51 case 0:return 70; 52 case 1:return 100; 53 case 2:return 80; 54 default:return 120; 55 } 56 } 57 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type 58 { 59 switch (type) { 60 case YYWaterflowViewMarginTypeTop: 61 case YYWaterflowViewMarginTypeBottom: 62 case YYWaterflowViewMarginTypeLeft: 63 case YYWaterflowViewMarginTypeRight: 64 return 10; 65 case YYWaterflowViewMarginTypeColumn: 66 case YYWaterflowViewMarginTypeRow: 67 return 20; 68 } 69 } 70 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index 71 { 72 NSLog(@"点击了%d的cell",index); 73 } 74 @end
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
iOS开发UI篇—自定义瀑布流控件(基本实现)
一、基本实现
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 100; 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 //给cell设置一个随机色 45 cell.backgroundColor=YYRandomColor; 46 return cell; 47 } 48 49 50 #pragma mark-代理方法 51 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index 52 { 53 switch (index%3) { 54 case 0:return 90; 55 case 1:return 110; 56 case 2:return 80; 57 default:return 120; 58 } 59 } 60 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type 61 { 62 switch (type) { 63 case YYWaterflowViewMarginTypeTop: 64 case YYWaterflowViewMarginTypeBottom: 65 case YYWaterflowViewMarginTypeLeft: 66 case YYWaterflowViewMarginTypeRight: 67 return 10; 68 case YYWaterflowViewMarginTypeColumn: 69 case YYWaterflowViewMarginTypeRow: 70 return 5; 71 } 72 } 73 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index 74 { 75 NSLog(@"点击了%d的cell",index); 76 } 77 @end
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 /** 84 * 刷新数据 85 */ 86 -(void)reloadData; 87 @end
瀑布流的内部实现(计算每个cell的frame)
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 @property(nonatomic,strong)NSMutableArray *cellFrames; 17 @end 18 19 @implementation YYWaterflowView 20 21 #pragma mark-懒加载 22 -(NSMutableArray *)cellFrames 23 { 24 if (_cellFrames==nil) { 25 _cellFrames=[NSMutableArray array]; 26 } 27 return _cellFrames; 28 } 29 30 - (id)initWithFrame:(CGRect)frame 31 { 32 self = [super initWithFrame:frame]; 33 if (self) { 34 } 35 return self; 36 } 37 38 /** 39 * 刷新数据 40 * 1.计算每个cell的frame 41 */ 42 -(void)reloadData 43 { 44 //cell的总数是多少 45 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self]; 46 47 //cell的列数 48 int numberOfColumns=[self numberOfColumns]; 49 50 //间距 51 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 52 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight]; 53 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 54 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop]; 55 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow]; 56 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom]; 57 58 //(1)cell的宽度 59 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数 60 CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 61 62 63 64 //用一个C语言的数组来存放所有列的最大的Y值 65 CGFloat maxYOfColumns[numberOfColumns]; 66 for (int i=0; i<numberOfColumns; i++) { 67 //初始化数组的数值全部为0 68 maxYOfColumns[i]=0.0; 69 } 70 71 72 //计算每个cell的fram 73 for (int i=0; i<numberOfCells; i++) { 74 75 //(2)cell的高度 76 //询问代理i位置的高度 77 CGFloat cellH=[self heightAtIndex:i]; 78 79 //cell处在第几列(最短的一列) 80 NSUInteger cellAtColumn=0; 81 82 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值) 83 //默认设置最短的一列为第一列(优化性能) 84 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn]; 85 86 //求出最短的那一列 87 for (int j=0; j<numberOfColumns; j++) { 88 if (maxYOfColumns[j]<maxYOfCellAtColumn) { 89 cellAtColumn=j; 90 maxYOfCellAtColumn=maxYOfColumns[j]; 91 } 92 } 93 94 //(3)cell的位置(X,Y) 95 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距) 96 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM); 97 //cell的Y,先设定为0 98 CGFloat cellY=0; 99 if (maxYOfCellAtColumn==0.0) {//首行 100 cellY=topM; 101 }else 102 { 103 cellY=maxYOfCellAtColumn+rowM; 104 } 105 106 //(4)设置cell的frame并添加到数组中 107 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH); 108 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]]; 109 110 //更新最短那一列的最大的Y值 111 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame); 112 113 //显示cell 114 YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i]; 115 cell.frame=cellFrame; 116 [self addSubview:cell]; 117 } 118 119 //设置contentSize 120 CGFloat contentH=maxYOfColumns[0]; 121 for (int i=1; i<numberOfColumns; i++) { 122 if (maxYOfColumns[i]>contentH) { 123 contentH=maxYOfColumns[i]; 124 } 125 } 126 contentH += bottomM; 127 self.contentSize=CGSizeMake(0, contentH); 128 } 129 130 #pragma mark-私有方法 131 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type 132 { 133 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) { 134 return [self.delegate waterflowView:self marginForType:type]; 135 }else 136 { 137 return YYWaterflowViewDefaultMargin; 138 } 139 } 140 141 -(NSUInteger)numberOfColumns 142 { 143 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) { 144 return [self.dadaSource numberOfColumnsInWaterflowView:self]; 145 }else 146 { 147 return YYWaterflowViewDefaultNumberOfClunms; 148 } 149 } 150 151 -(CGFloat)heightAtIndex:(NSUInteger)index 152 { 153 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) { 154 return [self.delegate waterflowView:self heightAtIndex:index]; 155 }else 156 { 157 return YYWaterflowViewDefaultCellH; 158 } 159 } 160 @end
实现的瀑布流效果:
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的创建数量:
iOS开发UI篇—自定义瀑布流控件(cell的事件处理)
一、关于cell的复用的补充
在设置每个索引位置对应的cell的方法中,打印cell的索引和地址,已查看cell的循环利用情况
1 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 2 { 3 // YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init]; 4 5 static NSString *ID=@"cell"; 6 YYWaterflowViewCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID]; 7 if (cell==nil) { 8 cell=[[YYWaterflowViewCell alloc]init]; 9 cell.identifier=ID; 10 //给cell设置一个随机色 11 cell.backgroundColor=YYRandomColor; 12 // [cell addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]]; 13 } 14 //通过取出cell中的label,重新为label的text赋值 15 16 NSLog(@"%d---%p",index,cell); 17 return cell; 18 }
查看:
二、事件处理
1.在cell创建的时候添加一个label,设置label的tag值,以便在之后能够获取到label。
代码示例:
YYViewController.m文件
1 // YYViewController.m 2 // 06-瀑布流 3 // 4 // Created by apple on 14-7-28. 5 // Copyright (c) 2014年 wendingding. All rights reserved. 6 // 7 8 #import "YYViewController.h" 9 #import "YYWaterflowView.h" 10 #import "YYWaterflowViewCell.h" 11 12 @interface YYViewController ()<YYWaterflowViewDelegate,YYWaterflowViewDataSource> 13 14 @end 15 16 @implementation YYViewController 17 18 - (void)viewDidLoad 19 { 20 [super viewDidLoad]; 21 YYWaterflowView *waterflow=[[YYWaterflowView alloc]init]; 22 waterflow.frame=self.view.bounds; 23 waterflow.delegate=self; 24 waterflow.dadaSource=self; 25 [self.view addSubview:waterflow]; 26 27 //刷新数据 28 [waterflow reloadData]; 29 } 30 31 #pragma mark-数据源方法 32 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView 33 { 34 return 40; 35 } 36 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView 37 { 38 return 3; 39 } 40 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 41 { 42 // YYWaterflowViewCell *cell=[[YYWaterflowViewCell alloc]init]; 43 44 static NSString *ID=@"cell"; 45 YYWaterflowViewCell *cell=[waterflowView dequeueReusableCellWithIdentifier:ID]; 46 if (cell==nil) { 47 cell=[[YYWaterflowViewCell alloc]init]; 48 cell.identifier=ID; 49 //给cell设置一个随机色 50 cell.backgroundColor=YYRandomColor; 51 // [cell addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]]; 52 //在cell中添加一个label,设置tag值 53 UILabel *label=[[UILabel alloc]init]; 54 label.tag=10; 55 //注意:需要设置控件的frame值,否则不会显示 56 label.frame=CGRectMake(0, 0, 20, 20); 57 [cell addSubview:label]; 58 } 59 //通过取出cell中的label,重新为label的text赋值 60 UILabel *label=(UILabel *)[cell viewWithTag:10]; 61 label.text=[NSString stringWithFormat:@"%d",index]; 62 63 NSLog(@"%d---%p",index,cell); 64 return cell; 65 } 66 67 68 #pragma mark-代理方法 69 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index 70 { 71 switch (index%3) { 72 case 0:return 90; 73 case 1:return 110; 74 case 2:return 80; 75 default:return 120; 76 } 77 } 78 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type 79 { 80 switch (type) { 81 case YYWaterflowViewMarginTypeTop: 82 case YYWaterflowViewMarginTypeBottom: 83 case YYWaterflowViewMarginTypeLeft: 84 case YYWaterflowViewMarginTypeRight: 85 return 10; 86 case YYWaterflowViewMarginTypeColumn: 87 case YYWaterflowViewMarginTypeRow: 88 return 5; 89 } 90 } 91 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index 92 { 93 NSLog(@"点击了第%d个cell",index); 94 } 95 @end
2.在YYWaterflowView.m中,监听手指对瀑布流的触碰,获得手指在屏幕上点击的触摸点,判断该触摸点是否在cell上,在哪个cell上?
如果点击了cell调用代理方法,那么处理对应cell的点击事件。
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 #pragma mark-公共方法 66 /** 67 * 刷新数据 68 * 1.计算每个cell的frame 69 */ 70 -(void)reloadData 71 { 72 //cell的总数是多少 73 int numberOfCells=[self.dadaSource numberOfCellsInWaterflowView:self]; 74 75 //cell的列数 76 int numberOfColumns=[self numberOfColumns]; 77 78 //间距 79 CGFloat leftM=[self marginForType:YYWaterflowViewMarginTypeLeft]; 80 CGFloat rightM=[self marginForType:YYWaterflowViewMarginTypeRight]; 81 CGFloat columnM=[self marginForType:YYWaterflowViewMarginTypeColumn]; 82 CGFloat topM=[self marginForType:YYWaterflowViewMarginTypeTop]; 83 CGFloat rowM=[self marginForType:YYWaterflowViewMarginTypeRow]; 84 CGFloat bottomM=[self marginForType:YYWaterflowViewMarginTypeBottom]; 85 86 //(1)cell的宽度 87 //cell的宽度=(整个view的宽度-左边的间距-右边的间距-(列数-1)X每列之间的间距)/总列数 88 CGFloat cellW=(self.frame.size.width-leftM-rightM-(numberOfColumns-1)*columnM)/numberOfColumns; 89 90 91 92 //用一个C语言的数组来存放所有列的最大的Y值 93 CGFloat maxYOfColumns[numberOfColumns]; 94 for (int i=0; i<numberOfColumns; i++) { 95 //初始化数组的数值全部为0 96 maxYOfColumns[i]=0.0; 97 } 98 99 100 //计算每个cell的fram 101 for (int i=0; i<numberOfCells; i++) { 102 103 //(2)cell的高度 104 //询问代理i位置的高度 105 CGFloat cellH=[self heightAtIndex:i]; 106 107 //cell处在第几列(最短的一列) 108 NSUInteger cellAtColumn=0; 109 110 //cell所处那列的最大的Y值(当前最短的那一列的最大的Y值) 111 //默认设置最短的一列为第一列(优化性能) 112 CGFloat maxYOfCellAtColumn=maxYOfColumns[cellAtColumn]; 113 114 //求出最短的那一列 115 for (int j=0; j<numberOfColumns; j++) { 116 if (maxYOfColumns[j]<maxYOfCellAtColumn) { 117 cellAtColumn=j; 118 maxYOfCellAtColumn=maxYOfColumns[j]; 119 } 120 } 121 122 //(3)cell的位置(X,Y) 123 //cell的X=左边的间距+列号*(cell的宽度+每列之间的间距) 124 CGFloat cellX=leftM+cellAtColumn*(cellW +columnM); 125 //cell的Y,先设定为0 126 CGFloat cellY=0; 127 if (maxYOfCellAtColumn==0.0) {//首行 128 cellY=topM; 129 }else 130 { 131 cellY=maxYOfCellAtColumn+rowM; 132 } 133 134 //设置cell的frame并添加到数组中 135 CGRect cellFrame=CGRectMake(cellX, cellY, cellW, cellH); 136 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]]; 137 138 //更新最短那一列的最大的Y值 139 maxYOfColumns[cellAtColumn]=CGRectGetMaxY(cellFrame); 140 141 //显示cell 142 // YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i]; 143 // cell.frame=cellFrame; 144 // [self addSubview:cell]; 145 } 146 147 //设置contentSize 148 CGFloat contentH=maxYOfColumns[0]; 149 for (int i=1; i<numberOfColumns; i++) { 150 if (maxYOfColumns[i]>contentH) { 151 contentH=maxYOfColumns[i]; 152 } 153 } 154 contentH += bottomM; 155 self.contentSize=CGSizeMake(0, contentH); 156 } 157 158 /** 159 * 当UIScrollView滚动的时候也会调用这个方法 160 */ 161 -(void)layoutSubviews 162 { 163 [super layoutSubviews]; 164 165 166 //向数据源索要对应位置的cell 167 NSUInteger numberOfCells=self.cellFrames.count; 168 for (int i=0; i<numberOfCells; i++) { 169 //取出i位置的frame,注意转换 170 CGRect cellFrame=[self.cellFrames[i] CGRectValue]; 171 172 //优先从字典中取出i位置的cell 173 YYWaterflowViewCell *cell=self.displayingCells[@(i)]; 174 175 //判断i位置对应的frame在不在屏幕上(能否看见) 176 if ([self isInScreen:cellFrame]) {//在屏幕上 177 if (cell==nil) { 178 cell= [self.dadaSource waterflowView:self cellAtIndex:i]; 179 cell.frame=cellFrame; 180 [self addSubview:cell]; 181 182 //存放在字典中 183 self.displayingCells[@(i)]=cell; 184 } 185 186 }else //不在屏幕上 187 { 188 if (cell) { 189 //从scrollView和字典中删除 190 [cell removeFromSuperview]; 191 [self.displayingCells removeObjectForKey:@(i)]; 192 193 //存放进缓存池 194 [self.reusableCells addObject:cell]; 195 } 196 } 197 } 198 // NSLog(@"%d",self.subviews.count); 199 } 200 201 -(id)dequeueReusableCellWithIdentifier:(NSString *)identifier 202 { 203 __block YYWaterflowViewCell *reusableCell=nil; 204 [self.reusableCells enumerateObjectsUsingBlock:^(YYWaterflowViewCell *cell, BOOL *stop) { 205 if ([cell.identifier isEqualToString:identifier]) { 206 reusableCell=cell; 207 *stop=YES; 208 } 209 }]; 210 211 if (reusableCell) {//从缓存池中移除(已经用掉了) 212 [self.reusableCells removeObject:reusableCell]; 213 } 214 return reusableCell; 215 } 216 217 #pragma mark cell的事件处理 218 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 219 { 220 //如果没有点击事件的代理方法,那么就直接返回 221 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) 222 return; 223 224 //获得手指在屏幕上点击的触摸点 225 UITouch *touch=[touches anyObject]; 226 // CGPoint point=[touch locationInView:touch.view]; 227 CGPoint point=[touch locationInView:self]; 228 229 __block NSNumber *selectIndex=nil; 230 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, YYWaterflowViewCell *cell, BOOL *stop) { 231 if (CGRectContainsPoint(cell.frame, point)) { 232 selectIndex=key; 233 *stop=YES; 234 } 235 }]; 236 if (selectIndex) { 237 //需要转换 238 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue]; 239 } 240 241 } 242 #pragma mark-私有方法 243 /** 244 * 判断一个人cell的frame有没有显示在屏幕上 245 */ 246 -(BOOL)isInScreen:(CGRect)frame 247 { 248 // return (CGRectGetMaxY(frame)>self.contentOffset.y)&&(CGRectGetMaxY(frame)<self.contentOffset.y+self.frame.size.height); 249 return (CGRectGetMaxY(frame) > self.contentOffset.y) && 250 (CGRectGetMinY(frame) < self.contentOffset.y + self.frame.size.height); 251 252 } 253 -(CGFloat)marginForType:(YYWaterflowViewMarginType)type 254 { 255 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) { 256 return [self.delegate waterflowView:self marginForType:type]; 257 }else 258 { 259 return YYWaterflowViewDefaultMargin; 260 } 261 } 262 263 -(NSUInteger)numberOfColumns 264 { 265 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) { 266 return [self.dadaSource numberOfColumnsInWaterflowView:self]; 267 }else 268 { 269 return YYWaterflowViewDefaultNumberOfClunms; 270 } 271 } 272 273 -(CGFloat)heightAtIndex:(NSUInteger)index 274 { 275 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) { 276 return [self.delegate waterflowView:self heightAtIndex:index]; 277 }else 278 { 279 return YYWaterflowViewDefaultCellH; 280 } 281 } 282 @end
实现效果:
点击对应的cell,能够监听并对点击事件进行处理。
三、补充说明
示例代码:
说明:
touch.view指的是子控件(单个cell)以这个cell的左上角为(0,0)进行坐标计算
self指的是整个瀑布流,以整个瀑布流的左上角为(0,0)进行坐标计算
打印查看:
=======================================================================iOS开发UI篇—自定义瀑布流控件(蘑菇街瀑布流)
一、简单说明
关于瀑布流
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
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
3.运行效果
说明:已经实现了cell的循环利用。
iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)
一、简单说明
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 //初始化数组的数值全部为0 125 maxYOfColumns[i]=0.0; 126 } 127 128 129 //计算每个cell的fram 130 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,先设定为0 155 CGFloat cellY=0; 156 if (maxYOfCellAtColumn==0.0) {//首行 157 cellY=topM; 158 }else 159 { 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 //设置contentSize 172 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)layoutSubviews 186 { 187 [super layoutSubviews]; 188 189 190 //向数据源索要对应位置的cell 191 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位置的cell 197 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 *)identifier 226 { 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 *)event 243 { 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)frame 272 { 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)type 279 { 280 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) { 281 return [self.delegate waterflowView:self marginForType:type]; 282 }else 283 { 284 return YYWaterflowViewDefaultMargin; 285 } 286 } 287 288 -(NSUInteger)numberOfColumns 289 { 290 if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) { 291 return [self.dadaSource numberOfColumnsInWaterflowView:self]; 292 }else 293 { 294 return YYWaterflowViewDefaultNumberOfClunms; 295 } 296 } 297 298 -(CGFloat)heightAtIndex:(NSUInteger)index 299 { 300 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) { 301 return [self.delegate waterflowView:self heightAtIndex:index]; 302 }else 303 { 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 *)waterflowView 105 { 106 return self.shops.count; 107 } 108 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView 109 { 110 return 3; 111 } 112 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index 113 { 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)index 122 { 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)index 129 { 130 NSLog(@"点击了第%d个cell",index); 131 } 132 133 134 @end
实现的刷新效果: