首页 > 代码库 > iOS开发UI篇—自定义瀑布流控件(基本实现)

iOS开发UI篇—自定义瀑布流控件(基本实现)

iOS开发UI篇—自定义瀑布流控件(基本实现)

一、基本实现

说明:在View加载的时候,刷新数据。
 
1.实现代码
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 100;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     //给cell设置一个随机色45     cell.backgroundColor=YYRandomColor;46     return cell;47 }48 49 50 #pragma mark-代理方法51 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index52 {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)type61 {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)index74 {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 @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 /**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         }else102         {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         //显示cell114         YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];115         cell.frame=cellFrame;116         [self addSubview:cell];117     }118     119     //设置contentSize120     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)type132 {133     if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {134        return  [self.delegate waterflowView:self marginForType:type];135     }else136     {137         return YYWaterflowViewDefaultMargin;138     }139 }140 141 -(NSUInteger)numberOfColumns142 {143     if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {144         return [self.dadaSource numberOfColumnsInWaterflowView:self];145     }else146     {147         return  YYWaterflowViewDefaultNumberOfClunms;148     }149 }150 151 -(CGFloat)heightAtIndex:(NSUInteger)index152 {153     if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {154         return [self.delegate waterflowView:self heightAtIndex:index];155     }else156     {157         return YYWaterflowViewDefaultCellH;158     }159 }160 @end

实现的瀑布流效果:

     

2.简单说明
说明
(1)瀑布流每一个的宽度是一样的,都是高度不一样
(2)补齐算法,哪里比较短就补哪里,不是简单的从左到右排(两列之间的差距越来越大)。
这就要求我们时刻知道每一列最大的Y值是多少,以比较哪里“最短”。
可以考虑使用一个C语言的数组来存放所有列的最大Y值
注意数组的初始化操作。
 
提示:瀑布流的最后一行一般都是参差不齐的。
可扩展性:
简单的修改cell的列数,即可修改布局。
(1)设置瀑布流为2列。
(2)设置瀑布流的列数为4列
(3)如果不设置列数,那么显示的列数默认为3列。
(4)如果不设置高度,那么显示的cell的高度为默认的高度,都是一样的。
  
(5)cell的上下左右,行和列之间的间距也可以进行调整,这里不做演示。
(6)在cell中可以添加自定义的控件,如Button、imageView等,此时可以向使用UITableView和UITableViewcell一样来使用YYWaterflowView和YYWaterflowViewCell。
 
3.存在的问题
  上面的代码对cell的处理存在很大的性能问题,如果程序中又2000个cell,那么这里就创建了两千个cell,性能很差。
  可以通过在layoutSubviews方法中打印查看。
 
说明:之所以为2002,是因为创建了2000个cell+2个滚动条(水平方向上的滚动条被隐藏了,但是仍然存在)
优化思路:放入到缓存池。