您的位置:首页 > 移动开发 > IOS开发

iOS—自定义瀑布流控件

2014-08-20 12:48 148 查看
一、简单说明

使用数据刷新框架:



该框架提供了两种刷新的方法,一个是使用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




实现的刷新效果:

  




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: