CollectionView实现瀑布流布局

一.说明

新建一个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;
}
...

 

posted on 2016-04-30 13:04 远离舒适圈 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/decomfor/p/5448618.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值