#import <UIKit/UIKit.h>
@class UICollectionViewFlowWaterLayout;
@protocol UICollectionViewFlowWaterDelegate <UICollectionViewDelegate>
- (CGFloat)collectionView:(UICollectionView *)c layout:(UICollectionViewFlowWaterLayout *)collectionViewLayout heightForItemAtIndextPath:(NSIndexPath*)p;
//这里需要代理返回区头的高
-(CGFloat)collectionView:(UICollectionView *)c layoutWithHeader:(UICollectionViewFlowWaterLayout *)collectionViewLayout heightForHeaderAtIndextPath:(NSIndexPath*)indexPath;
@end
@interface UICollectionViewFlowWaterLayout : UICollectionViewLayout
@property (nonatomic,assign) NSInteger HeaderHeight;
//列数
@property (nonatomic,assign) NSInteger columnCount;
//每一个 item 的宽度
@property (nonatomic,assign) CGFloat itemWidth;
//每个 section 的偏移量
@property (nonatomic,assign) UIEdgeInsets sectionInset;
@property (nonatomic,assign) id<UICollectionViewFlowWaterDelegate> delegate;
@end
------------------------------------
#import "UICollectionViewFlowWaterLayout.h"
@interface UICollectionViewFlowWaterLayout ()
//记录当前的item 个数
@property (nonatomic,assign) NSInteger itemCount;
//记录间隙的宽度
@property (nonatomic,assign) CGFloat interitemSpacing;
//用来记录每一列的高度
@property (nonatomic,retain) NSMutableArray *columnHeights;
@property (nonatomic,retain) NSMutableArray *attributes;
@property (nonatomic,assign)NSInteger HeaderWithHeight;
@end
@implementation UICollectionViewFlowWaterLayout
- (void)dealloc {
self.columnHeights = nil;
self.attributes = nil;
[super dealloc];
}
- (id)init {
self = [super init];
if (self) {
[self initData];
}
return self;
}
- (void)initData {
_itemWidth = 140.0f;
_columnCount = 2;
_sectionInset = UIEdgeInsetsZero;
}
- (void)setColumnCount:(NSInteger)columnCount {
if (_columnCount != columnCount) {
_columnCount = columnCount;
//调用 ‘invalidateLayout’ 不会使布局立即响应, collection View 会检查当前布局是否是最新布局,如果不是则更新。也就是说多次调用 ‘invalidateLayout’ 不会引起布局的重复更新;
[self invalidateLayout];
}
}
- (void)setItemWidth:(CGFloat)itemWidth {
if (_itemWidth != itemWidth) {
_itemWidth = itemWidth;
[self invalidateLayout];
}
}
- (void)setSectionInset:(UIEdgeInsets)sectionInset {
if (!UIEdgeInsetsEqualToEdgeInsets(_sectionInset, sectionInset)) {
_sectionInset = sectionInset;
[self invalidateLayout];
}
}
#pragma mark - Override
//预先计算需要提供 布局信息 告诉 collection View 每个 item 的绘画属性
//用layout 对象调用invalidateLayout 来开始布局过程,首先会调用prepareLayout
- (void)prepareLayout {
[super prepareLayout];
//首先要获取collection View 当前section上的item 的个数
_itemCount = [[self collectionView] numberOfItemsInSection:0];
//断言 不足一列 就报错
NSAssert(_columnCount > 1, @"当前瀑布流的列数必须大于1");
//开始布局设置每一个item 的位置坐标和属性绘画
//拿到当前绘制collection view 内容的宽度
//也即是在多宽的位置上绘制
CGFloat width = self.collectionView.frame.size.width - _sectionInset.left -_sectionInset.right;
//计算一个间隙的宽度
_interitemSpacing = floorf(width - _columnCount*_itemWidth)/(_columnCount - 1);
self.attributes = [NSMutableArray arrayWithCapacity:_itemCount];
self.columnHeights = [NSMutableArray arrayWithCapacity:_columnCount];
for (NSInteger idx = 0; idx < _columnCount; idx++) {
// _columnHeights[idx] = @(_sectionInset.top);
[_columnHeights addObject:@(_sectionInset.top)];
}
NSLog(@"%@",_columnHeights);
//配置 每一个item 的绘制属性
//遍历下标
for (NSInteger idx = 0; idx < _itemCount; idx++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:0];
//拿到item 的高度
CGFloat itemHeight = [self.delegate collectionView:self.collectionView layout:self heightForItemAtIndextPath:indexPath];
//获取最低列的位置
NSInteger columnIdex = [self getMinHeightColumnIndex];
//设置没有个item 的xy
CGFloat xOffset = _sectionInset.left + (_itemWidth+_interitemSpacing)*columnIdex;
CGFloat yOffset = [(_columnHeights[columnIdex]) floatValue];
//设置每一个item 的绘画特性
//区头的高,非常重要,添加到attributesItem上面
_HeaderWithHeight =[self.delegate collectionView:self.collectionView layoutWithHeader:self heightForHeaderAtIndextPath:indexPath];
UICollectionViewLayoutAttributes *attributesItem = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributesItem.frame = CGRectMake(xOffset, yOffset+_HeaderWithHeight, _itemWidth, itemHeight);
[_attributes addObject:attributesItem];
_columnHeights[columnIdex] = @(yOffset + itemHeight + _interitemSpacing);
}
}
- (CGSize)collectionViewContentSize {
if (0 == self.itemCount) {
return CGSizeZero;
}
//这里需要返回一下区头的高度不然拉表的时候会有问题
CGSize contentSize = self.collectionView.frame.size;
NSInteger columnMaxIndex = [self getMaxHeightColumnIndex];
contentSize.height = [self.columnHeights[columnMaxIndex] floatValue] + self.sectionInset.bottom - self.interitemSpacing + _HeaderWithHeight;
return contentSize;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return (self.attributes)[indexPath.item];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return [self.attributes filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return CGRectIntersectsRect(rect,((UICollectionViewLayoutAttributes *)evaluatedObject).frame);
}]];
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return NO;
}
//find out min column index
- (NSInteger)getMinHeightColumnIndex {
__block NSInteger index = 0;
__block CGFloat tempIndex = MAXFLOAT;
[self.columnHeights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CGFloat temp = [obj floatValue];
if (temp < tempIndex) {
tempIndex = temp;
index = idx;
}
}];
return index;
}
//find out max column index
- (NSInteger)getMaxHeightColumnIndex {
__block NSInteger index = 0;
__block CGFloat tempIndex = 0;
NSLog(@"%@",self.columnHeights);
[self.columnHeights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CGFloat temp = [obj floatValue];
if (temp > tempIndex) {
tempIndex = temp;
index = idx;
}
}];
return index;
}
@end
--------具体的实现-----上面是自定义的cell
#import "ViewController.h"
#import "UICollectionViewFlowWaterLayout.h"
@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewFlowWaterDelegate>{
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowWaterLayout *layout = [[UICollectionViewFlowWaterLayout alloc]init];
layout.columnCount = 2;
layout.itemWidth = 150.0f;
layout.delegate = self;
UICollectionView *collection = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:layout];
[collection registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"kCellID"];
collection.backgroundColor = [UIColor redColor];
collection.dataSource = self;
collection.delegate = self;
[self.view addSubview:collection];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 10;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"kCellID" forIndexPath:indexPath];
cell.backgroundColor = [UIColor yellowColor];
return cell;
}
#pragma mark --------------代理-----------------
//这里自定义区头的高度,同理可以自定义区尾这里非常重要
-(CGFloat)collectionView:(UICollectionView *)c layoutWithHeader:(UICollectionViewFlowWaterLayout *)collectionViewLayout heightForHeaderAtIndextPath:(NSIndexPath*)indexPath
{
CGFloat height = arc4random()%200 + 100.0f;
return height;
}
- (CGFloat)collectionView:(UICollectionView *)c layout:(UICollectionViewFlowWaterLayout *)collectionViewLayout heightForItemAtIndextPath:(NSIndexPath *)p {
CGFloat height = arc4random()%200 + 50.0f;
return height;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"%ld",(long)indexPath.row);
}
@end