自定义UICollectionViewLayout实现瀑布流

该瀑布流的列数,列与列,行与行,距离四周的间距均通过代理由外界传入


//
//  ViewController.m
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import "ViewController.h"
#import "HQFlowLayout.h"
#import "HQCollectionViewCell.h"
#import <MJRefresh.h>
#import <MJExtension.h>
#import "HQShop.h"

@interface ViewController () <UICollectionViewDataSource,HQFlowLayoutDelegate>

@property(nonatomic,strong) NSMutableArray *goodsArrays;

@property(nonatomic,weak) UICollectionView *collectionView;


@end

static NSString * const cellID=@"flow_cell";

@implementation ViewController

-(NSMutableArray *)goodsArrays{
    
    if (_goodsArrays==nil) {
        _goodsArrays=[NSMutableArray array];
    }
    return _goodsArrays;
}

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    [self setUpCollection];
    
    [self setUpRefresh];
    
}


-(void) setUpCollection{
    
    HQFlowLayout *flow=[[HQFlowLayout alloc]init];
    
    flow.delegate=self;
    
    UICollectionView *collectionView=[[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:flow];
    
    collectionView.backgroundColor=[UIColor whiteColor];
    
    collectionView.dataSource=self;
    
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([HQCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:cellID];
    
    [self.view addSubview:collectionView];
    
    self.collectionView=collectionView;
    
}

-(void) setUpRefresh{
    
    //创建下拉刷新
    self.collectionView.mj_header=[MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
    [self.collectionView.mj_header beginRefreshing];
    
    //创建上拉刷新
    self.collectionView.mj_footer=[MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
    
}


-(void) loadNewData{
    
    [self.goodsArrays removeAllObjects];
    
    NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"];
    
    [self.goodsArrays addObjectsFromArray:array];
    
    [self.collectionView reloadData];
    
    [self.collectionView.mj_header endRefreshing];
}

-(void) loadMoreData{
    
    NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"];
    
    [self.goodsArrays addObjectsFromArray:array];
    
    [self.collectionView reloadData];
    
    [self.collectionView.mj_footer endRefreshing];
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    
    return self.goodsArrays.count;
}

-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    HQShop *shop=self.goodsArrays[indexPath.item];
    
    HQCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    
    cell.shop=shop;
    
    return cell;
}


#pragma mark 我们自定义的瀑布流HQFlowLayout当中的代理实现方法

//设置每个cell的高度
-(CGFloat)flowLayout:(HQFlowLayout *)flowLayout heightForItemAtIndex:(NSInteger)index itemWidth:(CGFloat)itemWidth{
    
    HQShop *shop=self.goodsArrays[index];
    
    //返回高度
    return shop.h*itemWidth/shop.w;
}

//设置cell的列数
-(CGFloat)columNumbersFlowLayout:(HQFlowLayout *)flowLayout{
    
    return 3;
}

//设置行与行之间的间距
-(CGFloat)rowMarginFlowLayout:(HQFlowLayout *)flowLayout{
    
    return 10;
}

//设置列与列之间的间距
-(CGFloat)colMarginFlowLayout:(HQFlowLayout *)flowLayout{
    
    return 10;
}

//设置四周边缘的距离
-(UIEdgeInsets)edgeIndsetsFlowLayout:(HQFlowLayout *)flowLayout{
    
    UIEdgeInsets sets=UIEdgeInsetsMake(10, 10, 10, 10);
    return sets;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
}

@end



核心方法如下

//
//  HQFlow.h
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import <UIKit/UIKit.h>
@class HQFlowLayout;

@protocol HQFlowLayoutDelegate <NSObject>

@required

//返回每行的高度
-(CGFloat) flowLayout:(HQFlowLayout *) flowLayout heightForItemAtIndex:(NSInteger) index itemWidth:(CGFloat) itemWidth;

@optional
//返回列数
-(CGFloat) columNumbersFlowLayout:(HQFlowLayout *) flowLayout;

//返回行与行之间的间距
-(CGFloat) rowMarginFlowLayout:(HQFlowLayout *) flowLayout;

//返回列与列之间的间距
-(CGFloat) colMarginFlowLayout:(HQFlowLayout *) flowLayout;

//设置距离边缘四周的距离
-(UIEdgeInsets) edgeIndsetsFlowLayout:(HQFlowLayout *) flowLayout;

@end


@interface HQFlowLayout : UICollectionViewLayout


@property(nonatomic,weak) id<HQFlowLayoutDelegate> delegate;


@end



//
//  HQFlow.m
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import "HQFlowLayout.h"
#import <MJExtension.h>
#import <MJRefresh.h>

@interface HQFlowLayout()

@property(nonatomic,strong) NSMutableArray *dataArrays;

@property(nonatomic,strong) NSMutableArray *colHeightArrays;

@end

@implementation HQFlowLayout

//有3列
static NSInteger colNumber=3;

//每行之间的间距
static CGFloat rowMargin=10;

//每列之间的间距
static CGFloat colMargin=10;

//距离边缘的间距
static UIEdgeInsets boderInsets={10,10,10,10};

#pragma mark 懒加载

-(NSMutableArray *)dataArrays{
    
    if (_dataArrays==nil) {
        _dataArrays=[NSMutableArray array];
    }
    return _dataArrays;
}

//保存每行的高度
-(NSMutableArray *)colHeightArrays{
    
    if (_colHeightArrays==nil) {
        _colHeightArrays=[NSMutableArray array];
    }
    return _colHeightArrays;
}

#pragma mark 通过代理获取属性,获取不到,则使用默认值

//获取列数
-(NSInteger) getColumNumber{
    
    if ([self.delegate respondsToSelector:@selector(columNumbersFlowLayout:)]) {
        
        return [self.delegate columNumbersFlowLayout:self];
    }
    
    //没有设置则用默认值
    return colNumber;
}

//获取行与行之间的间距
-(CGFloat) getRowMargin{
    
    if ([self.delegate respondsToSelector:@selector(rowMarginFlowLayout:)]) {
        
        return [self.delegate rowMarginFlowLayout:self];
    }
    
    //没有设置则用默认值
    return rowMargin;
    
}


//获取列与列之间的间距
-(CGFloat) getColMargin{
    
    if ([self.delegate respondsToSelector:@selector(colMarginFlowLayout:)]) {
        
        return [self.delegate colMarginFlowLayout:self];
    }
    
    //没有设置则用默认值
    return colMargin;
    
}

-(UIEdgeInsets) getEdgeInsets{
    
    if ([self.delegate respondsToSelector:@selector(edgeIndsetsFlowLayout:)]) {
        
        return [self.delegate edgeIndsetsFlowLayout:self];
    }
    
    //没有设置则用默认值
    return boderInsets;
    
}



//初始化操作,必须写super prepareLayout
//每次表格reloaddata,都会调用该方法
-(void)prepareLayout{
    
    [self.dataArrays removeAllObjects];
    
    [self.colHeightArrays removeAllObjects];
    
    [super prepareLayout];
    
     NSLog(@"%s",__func__);
    
    //初始化我们每个col的高度,默认为距离顶部的高度
    for (int i=0; i<[self getColumNumber]; i++) {
        
        [self.colHeightArrays addObject:@([self getEdgeInsets].top)];
    }
    
    NSInteger cellCount=[self.collectionView numberOfItemsInSection:0];
    
    //初始化我们所有cell属性的数组
    for (int i=0; i<cellCount; i++) {
        
        NSIndexPath *indexpath=[NSIndexPath indexPathForItem:i inSection:0];
        
        UICollectionViewLayoutAttributes *attr=[self layoutAttributesForItemAtIndexPath:indexpath];
        
        [self.dataArrays addObject:attr];
    }
}

//所有元素的属性
//该方法,每滚动一次就会被调用一次,因此我们把数组的初始化放到preparelayout当中去
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{

    return self.dataArrays;
}

-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    CGFloat w=(self.collectionView.bounds.size.width-[self getColMargin]*([self getColumNumber]-1)-[self getEdgeInsets].left-[self getEdgeInsets].right)/[self getColumNumber];
    
//    CGFloat h=arc4random_uniform(100)+80;
    
    //通过代理方法获取高度
    CGFloat h=[self.delegate flowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
    
    //接下来计算哪个列最短
    
    CGFloat minHeight=[self.colHeightArrays[0] doubleValue];
    
    NSInteger minCol=0;
    
    for (int i=1; i<[self getColumNumber]; i++) {
        
        CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
        
        if (currentHeight<minHeight) {
            minCol=i;
            minHeight=currentHeight;
        }
    }

    CGFloat x=[self getEdgeInsets].left+(w+[self getColMargin])*minCol;
    
    CGFloat y=minHeight+[self getRowMargin];
    
    attr.frame=CGRectMake(x, y, w, h);
    
    self.colHeightArrays[minCol]=@(CGRectGetMaxY(attr.frame));

    return attr;
    
}

-(CGSize)collectionViewContentSize{
    
   //取出我们的最大高度
    CGFloat maxHeight=[self.colHeightArrays[0] doubleValue];
    
    for (int i=1; i<[self getColumNumber]; i++) {
        
        CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
        
        if (currentHeight>maxHeight) {
            maxHeight=currentHeight;
        }
    }
    
    return CGSizeMake(0, maxHeight);
}


@end




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值