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

iOS开发UI篇—自定义瀑布流控件(蘑菇街实现)

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 @required30 /**31  * (1)一共有多少个数据32  */33 -(NSUInteger)numberOfCellsInWaterflowView:(YYWaterflowView *)waterflowView;34 /**35  *  (2)返回index位置对应的cell36  */37 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;38 39 //不要求强制实现40 @optional41 /**42  *  (3)一共有多少列43  */44 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView;45 46 @end47 48 49 /**50  *  2.代理方法51  */52 @protocol YYWaterflowViewDelegate <UIScrollViewDelegate>53 //不要求强制实现54 @optional55 /**56  *  (1)第index位置cell对应的高度57  */58 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;59 /**60  *  (2)选中第index位置的cell61  */62 -(void)waterflowView:(YYWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;63 /**64  *  (3)返回间距65  */66 -(CGFloat)waterflowView:(YYWaterflowView *)waterflowView marginForType:(YYWaterflowViewMarginType)type;67 @end68 69 70 /**71  *  3.瀑布流控件72  */73 @interface YYWaterflowView : UIScrollView74 /**75  *  (1)数据源76  */77 @property(nonatomic,weak)id<YYWaterflowViewDataSource> dadaSource;78 /**79  *  (2)代理80  */81 @property(nonatomic,weak)id<YYWaterflowViewDelegate> delegate;82 83 #pragma mark-公共方法84 /*85  *cell的宽度86  */87 -(CGFloat)cellWidth;88 /**89  *  刷新数据90  */91 -(void)reloadData;92 /**93  *  根据标识去缓存池中查找可循环利用的cell94  */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         //初始化数组的数值全部为0113         maxYOfColumns[i]=0.0;114     }115     116     117     //计算每个cell的fram118     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,先设定为0143         CGFloat cellY=0;144         if (maxYOfCellAtColumn==0.0) {//首行145             cellY=topM;146         }else147         {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         //显示cell159 //        YYWaterflowViewCell *cell=[self.dadaSource waterflowView:self cellAtIndex:i];160 //        cell.frame=cellFrame;161 //        [self addSubview:cell];162     }163     164     //设置contentSize165     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)layoutSubviews179 {180     [super layoutSubviews];181  182     183     //向数据源索要对应位置的cell184     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位置的cell190         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 *)identifier219 {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 *)event236 {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)frame265 {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)type272 {273     if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {274        return  [self.delegate waterflowView:self marginForType:type];275     }else276     {277         return YYWaterflowViewDefaultMargin;278     }279 }280 281 -(NSUInteger)numberOfColumns282 {283     if ([self.dadaSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {284         return [self.dadaSource numberOfColumnsInWaterflowView:self];285     }else286     {287         return  YYWaterflowViewDefaultNumberOfClunms;288     }289 }290 291 -(CGFloat)heightAtIndex:(NSUInteger)index292 {293     if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {294         return [self.delegate waterflowView:self heightAtIndex:index];295     }else296     {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 : NSObject12 /**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 @end19 20 @implementation YYShopViewController21 22 #pragma mark-懒加载23 -(NSMutableArray *)shops24 {25     if (_shops==nil) {26         _shops=[NSMutableArray array];27     }28     return _shops;29 }30 - (void)viewDidLoad31 {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 *)waterflowView46 {47     return 40;48 }49 -(NSUInteger)numberOfColumnsInWaterflowView:(YYWaterflowView *)waterflowView50 {51     return 3;52 }53 -(YYWaterflowViewCell *)waterflowView:(YYWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index54 {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)index63 {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)index70 {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 : YYWaterflowViewCell12 @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 @end19 @implementation YYShopCell20 21 - (id)initWithFrame:(CGRect)frame22 {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 *)waterflowView41 {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 *)shop52 {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)layoutSubviews59 {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_010 #warning "This project uses features only available in iOS SDK 5.0 and later."11 #endif12 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的循环利用。