iOS—自定义瀑布流控件

一、简单说明

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

实现的刷新效果:

           

iOS—自定义瀑布流控件,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。