ios自定义瀑布流控件

iOS开发UI篇—自定义瀑布流控件(接口设计)

一、简单说明

1.关于瀑布流

  

电商应用要展示商品信息通常是通过瀑布流的方式,因为每个商品的展示图片,长度和商都都不太一样。

如果不用瀑布流的话,展示这样的格子数据,还有一种办法是使用九宫格。

但利用九宫格有一个缺点,那就是每个格子的宽高是一样的,如果一定要使用九宫格来展示,那么展示的商品图片可能会变形。

为了保证商品图片能够按照原来的宽高比进行展示,一般采用的是瀑布流的方式。

2.瀑布流的特点:
由很多的格子组成,但是每个格子的宽度和高速都是不确定的,是在动态改变的,就像瀑布一样,是一条线一条线的。
说明:使用tableView不能实现瀑布流式的布局,因为tableView是以行为单位的,它要求每行(cell)的高度在内部是一致的。
本系列文章介绍了如何自定义一个瀑布流控件来展示商品信息,本文介绍自定义瀑布流的接口设计。
 
3.自定义瀑布流控件的实现思路
  参考UITbaleView控件的设计。
  
(1)设置数据源(强制的,可选的)
1)告诉有多少个数据(cell)
2)每一个索引对应的cell
3)告诉显示多少列
(2)设置代理
代理方法都是可选的。
1)设置第index位置对应的高度
2)监听选中了第index的cell(控件)
3)设计返回的间距那么
 
二、自定义瀑布流控件(接口设计
1.新建一个项目
  
2.新建一个类,继承自UIScrollView,自己写一个瀑布流控件。
  
  
3.接口设计
 YYWaterflowView.h文件的代码设计
复制代码
 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篇—自定义瀑布流控件(基本实现)

一、基本实现

说明:在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 @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
复制代码

实现的瀑布流效果:

     

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个滚动条(水平方向上的滚动条被隐藏了,但是仍然存在)
优化思路:放入到缓存池

=======================================================================

iOS开发UI篇—自定义瀑布流控件(cell的循环利用)

一、简单说明

  当滚动的时候,向数据源要cell。

  当UIScrollView滚动的时候会调用layoutSubviews在tableView中也是一样的,因此,可以用这个方法来监听scrollView的滚动,可以在在这个地方向数据源索要对应位置的cell(frame在屏幕上的cell)。
示例:
  当scrollView在屏幕上滚动的时候,离开屏幕的cell应该放到缓存池中去,询问即将(已经)进入到屏幕的cell,对于还没有进入到屏幕的cell不作处理。
 
判断cell有没有在屏幕上?
  cell的最大的Y值>contentoffset的y值,并且小于contentoffset的y值+UIView的高度

 代码示例:

复制代码
 1 /**
 2  *  当UIScrollView滚动的时候也会调用这个方法
 3  */
 4 -(void)layoutSubviews
 5 {
 6     [super layoutSubviews];
 7     NSLog(@"%d",self.subviews.count);
 8     
 9     //向数据源索要对应位置的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.是使用UIScrollView实现的
2.刷新数据(reloadData)方法里面做哪些事情
3.layoutSubviews方法里面做哪些事情
4.模仿UItableView进行设计
完善:
瀑布流控件第一次显示到屏幕上的时候自动的向数据源索要数据,而不需要手动调用。这需要监听View的显示,View的显示有一个方法,叫做willMoveToSuperview:在该方法中直接刷新一次数据即可。
   
 
二、把自定义的瀑布流控件作为一个框架进行使用
1.框架
把自定义的瀑布流控件转变成一个框架。以后可以把它作为一套框架进行开发。
   
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文件的数据结构
  
新建一个shop模型,继承自NSObject类,
该类中的代码设计如下:
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
复制代码

3.运行效果

说明:已经实现了cell的循环利用。

==============================================

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)

一、简单说明

使用数据刷新框架:
该框架提供了两种刷新的方法,一个是使用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         //初始化数组的数值全部为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
复制代码

实现的刷新效果:

           

三、竖屏和横屏调整
设置横屏和竖屏。
屏幕旋转完毕会调用下面的方法。
因为scrollView的宽度是固定的,没有改变。
设置view的宽度和高度可以跟随者父控件自动拉伸。(在iPad开发中会将常用到)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值