首页 > 代码库 > iOS—自定义瀑布流控件

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         //初始化数组的数值全部为0125         maxYOfColumns[i]=0.0;126     }127     128     129     //计算每个cell的fram130     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,先设定为0155         CGFloat cellY=0;156         if (maxYOfCellAtColumn==0.0) {//首行157             cellY=topM;158         }else159         {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     //设置contentSize172     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)layoutSubviews186 {187     [super layoutSubviews];188  189     190     //向数据源索要对应位置的cell191     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位置的cell197         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 *)identifier226 {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 *)event243 {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)frame272 {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)type279 {280     if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {281        return  [self.delegate waterflowView:self marginForType:type];282     }else283     {284         return YYWaterflowViewDefaultMargin;285     }286 }287 288 -(NSUInteger)numberOfColumns289 {290     if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {291         return [self.dadaSource numberOfColumnsInWaterflowView:self];292     }else293     {294         return  YYWaterflowViewDefaultNumberOfClunms;295     }296 }297 298 -(CGFloat)heightAtIndex:(NSUInteger)index299 {300     if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {301         return [self.delegate waterflowView:self heightAtIndex:index];302     }else303     {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 *)waterflowView105 {106     return self.shops.count;107 }108 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView109 {110     return 3;111 }112 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index113 {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)index122 {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)index129 {130     NSLog(@"点击了第%d个cell",index);131 }132 133 134 @end

实现的刷新效果: