您的位置:首页 > 其它

CollectionView实现瀑布流布局

2016-04-30 13:04 323 查看
一.说明

新建一个Single View Application,删除main.stroryboard中原有的控制器,拖入一个新的CollectionViewController;

在原有的ViewController.h文件中,将继承改成UICollectionViewController,且绑定CollectionViewController.

在原型cell中拖入imageView控件和label控件,并设置相应约束.创建相应继承于UICollectionViewCell的cell文件,并绑定原型cell,设置可重用ID



导入相应plist文件和图片(如果只是做练习,可以自己创建个plist文件,设置其不同宽高即可,图片用颜色代替)





因为每张图片的尺寸大小都不同,则每个item的布局都不相同,故创建继承与UICollectionViewFlowLayout的文件,并绑定main.storyboard的Flow Layout



所有的文件列表如下:



二.代码实现

//
//  HBGood.h 文件中
//  4.30 CollectionView瀑布流

#import <Foundation/Foundation.h>

@interface HBGood : NSObject
//根据plist文件创建相应实型
@property (nonatomic,copy)NSString *icon;
@property (nonatomic,copy)NSString *price;
@property (nonatomic,assign)float height;
@property (nonatomic,assign)float width;

//构造方法
-(instancetype)initWithDict:(NSDictionary *)dict;
+(instancetype)goodWithDict:(NSDictionary *)dict;
@end


//
//  HBGood.m 文件中
//  4.30 CollectionView瀑布流

#import "HBGood.h"

@implementation HBGood
-(instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}

+(instancetype)goodWithDict:(NSDictionary *)dict
{
return [[self alloc]initWithDict:dict];
}
@end


//
//  HBGoodCell.h 文件中
//

#import <UIKit/UIKit.h>
#import "HBGood.h"
@interface HBGoodCell : UICollectionViewCell
//设置HBGood属性,传入值时为cell内容赋值
@property (strong,nonatomic) HBGood* good;
@end


//
//  HBGoodCell.m 文件中
//  4.30 CollectionView瀑布流

#import "HBGoodCell.h"
@interface HBGoodCell ()
//cell中的图片接口
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
//cell中的文字接口
@property (weak, nonatomic) IBOutlet UILabel *priceLbl;

@end
@implementation HBGoodCell
-(void)setGood:(HBGood *)good{
_good = good;
self.iconView.image = [UIImage imageNamed:good.icon];
self.priceLbl.text = [NSString stringWithFormat:@"价格 : %@",good.price];
}
@end


//
//  HBGoodFlowLayout.h 文件中
//  4.30 CollectionView瀑布流

#import <UIKit/UIKit.h>

@interface HBGoodFlowLayout : UICollectionViewFlowLayout
//导入所有的good,从而计算出所有good的布局
@property (strong,nonatomic)NSArray *goods;

//设立一个列数属性,能从外部指定分为几列
@property (nonatomic,assign)int colNum;
@end


/*
HBGoodFlowLayout.m 文件中

瀑布流的思想:
每个商品都有其对应的宽高比,故使其宽度相同,对应的item高度就会不同,通过设置对应的layout
使其相互错开.
宽度右设定的列数决定

*/

#import "HBGoodFlowLayout.h"
#import "HBGood.h"
@interface HBGoodFlowLayout ()
//该属性用来存储所有item的布局信息
@property (strong,nonatomic)NSMutableArray *allAttributes;
//存储底部最大Y值来设定滚动范围
@property (assign,nonatomic)float maxY;
@end
@implementation HBGoodFlowLayout

//初始化attributes
-(NSMutableArray *)allAttributes{
if (_allAttributes == nil) {
_allAttributes = [NSMutableArray array];
}
return _allAttributes;
}
//改写系统准备布局的方法
-(void)prepareLayout{
/*
思路:建立一个数组,其个数等于列数,用来存储每列的下一个元素的Y值,取出Y值后立即更新到下一列的Y值
*/
float nextColYs[self.colNum];
//初始化该数组
for (int i = 0; i<self.colNum; i++) {
nextColYs[i] = 0;
}
CGFloat totalW = self.collectionView.frame.size.width;
//计算所有商品宽度
CGFloat goodW = (totalW - self.sectionInset.left - self.sectionInset.right - (self.colNum - 1) * self.minimumInteritemSpacing) / self.colNum;
//计算每个商品的frame
for (int i = 0; i<self.goods.count; i++) {
HBGood *good = self.goods[i];
//数组索引
int index = i % self.colNum;//计算按比例缩放后的高
CGFloat goodH = goodW * (good.height / good.width);
//从数组中取出对应的Y值
CGFloat goodY = nextColYs[index];
//当前商品x值 = 屏幕左边间距 + 左边的商品个数 * (商品宽度 + 商品间隔)
CGFloat goodX = index * (self.minimumInteritemSpacing + goodW) + self.sectionInset.left;

//创建collectionViewLayoutAttributes对象来存储每个商品对应的frame
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

attr.frame = CGRectMake(goodX, goodY, goodW, goodH);
//加入allAttributes数组中
[self.allAttributes addObject:attr];

//更新数组中的下一个Y值
nextColYs[index] = CGRectGetMaxY(attr.frame) + self.minimumLineSpacing;

}

//所有商品循环完后,拿出最大的Y值作为滚动范围
self.maxY = [self getMax:nextColYs];

}

//获得数组中做大值方法
-(float)getMax:(float *)arr{
float max = arr[0];
for (int i = 1; i<self.colNum; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
return max;
}
//该方法返回所有元素的布局信息
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.allAttributes;
}

//返回滚动范围
-(CGSize)collectionViewContentSize{
return CGSizeMake(0, self.maxY);
}

@end


//
//  ViewController.h 文件中
//  4.30 CollectionView瀑布流
//

#import <UIKit/UIKit.h>

@interface ViewController : UICollectionViewController

@end


//
//  ViewController.m 文件中
//  4.30 CollectionView瀑布流

#import "ViewController.h"
#import "HBGood.h"
#import "HBGoodCell.h"
#import "HBGoodFlowLayout.h"
@interface ViewController ()
//创建flowLayout的输出接口
@property (weak, nonatomic) IBOutlet HBGoodFlowLayout *flowLayout;
@property (strong,nonatomic)NSArray *goods;
@end

@implementation ViewController
static NSString *ID = @"GOOD";
#pragma mark - 懒加载实现
-(NSArray *)goods{
if (_goods == nil) {
NSString *path = [[NSBundle mainBundle]pathForResource:@"water_fall01" ofType:@"plist"];
NSArray *arr = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *mtArr=  [NSMutableArray array];
for (NSDictionary *dict in arr) {
HBGood *good = [HBGood goodWithDict:dict];
[mtArr addObject:good];
}
_goods = mtArr;
}
return _goods;
}

#pragma mark - ViewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
//设置列数
self.flowLayout.colNum = 4;
//设置CollectionView背景色
self.collectionView.backgroundColor = [UIColor whiteColor];
//将所有的商品信息赋给layout,让其计算出相应布局
self.flowLayout.goods = self.goods;
}

#pragma mark - 设定item数
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.goods.count;
}

#pragma mark - 设定item内容
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
HBGoodCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
cell.good = self.goods[indexPath.item];
return cell;

}

@end


以上,程序基本实现完毕.

三.优化

当用以上代码时,可能会出现如图分配不均的情况



可通过如下修改HBGoodFlowLayout.m中代码解决该问题

//
// HBGoodFlowLayout.m文件中
//
-(void)prepareLayout{
......
for (int i = 0; i<self.goods.count; i++) {
HBGood *good = self.goods[i];
//数组索引
int index = i % self.colNum;
//为了解决分配不平均,此时数组的索引就不能直接取值,而是通过每次取得最小Y值对应的索引   
int index = [self getMinIndex:nextColYs];

....
}

//添加获得数组中最小值的索引方法
-(int)getMinIndex:(float *)arr{
float min = arr[0];
int index = 0;
for (int i = 1; i<self.colNum; i++) {
if (min > arr[i]) {
min = arr[i];
index = i;
}
}
return index;
}
...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: