最近学习了瀑布流布局,是用collectionView实现的。首先说说设计思路,用collectionView做出来的瀑布流是固定行数或者列数的。以现在主流固定列数为例,每个item就是固定宽,不固定高,同时每个item之间的间距是固定的,那么每行的Y值计算就需要通过前一行的最短的item来确定。
既然是用collectionView实现,而collectionView的布局是通过他的layout来实现,那么就需要重写UICollectionViewLayout中的
-(void)prepareLayout,
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect,
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath,
-(CGSize)collectionViewContentSize。
具体实现如下:
#import <UIKit/UIKit.h>
@class CustomWaterflowLayout;
@protocol CustomWaterflowLayoutDelegate <NSObject>
@required
//必须实现的代理方法,返回每个item的高度
-(CGFloat)customWaterflow:(CustomWaterflowLayout *)layout heightForItemAtIndexPath:(NSInteger)index itemWidth:(CGFloat)itemWidth;
@optional
-(NSInteger)colCountInCustomWaterflow:(CustomWaterflowLayout *)layout;
-(CGFloat)colMarginInCustomWaterflow:(CustomWaterflowLayout *)layout;
-(CGFloat)rowMarginInCustomWaterflow:(CustomWaterflowLayout *)layout;
-(UIEdgeInsets)edgeInsetsInCustomWaterflow:(CustomWaterflowLayout *)layout;
@end
@interface CustomWaterflowLayout : UICollectionViewLayout
@property (nonatomic, weak) id<CustomWaterflowLayoutDelegate>delegate;
@end
#import "CustomWaterflowLayout.h"
//默认列数
static const NSInteger CustomDefaultColumnCount = 3;
//每一列之间的间距
static const CGFloat CustomDefaultColMargin = 10.0;
//每一行之间的间距
static const CGFloat CustomDefaultRowMargin = 10.0;
//边缘间距
static const UIEdgeInsets CustomDefaultEdgeInsets = {10,10,10,10};
@interface CustomWaterflowLayout ()
//存放所有cell的布局属性
@property (nonatomic, strong) NSMutableArray * attributesAarray;
//存放所有列的当前高度
@property (nonatomic, strong) NSMutableArray * colHeightArray;
//内容的高度
@property (nonatomic, assign) CGFloat contentHeight;
-(CGFloat)colMargin;
-(CGFloat)rowMargin;
-(NSInteger)colCount;
-(UIEdgeInsets)edgeInsets;
@end
@implementation CustomWaterflowLayout
#pragma mark 代理方法数据处理/常见数据处理
//这样写可以在代理方法中设置需要的布局数据,也可以有默认数据,是模仿tableView的代理实现
-(UIEdgeInsets)edgeInsets{
if ([self.delegate respondsToSelector:@selector(edgeInsetsInCustomWaterflow:)]) {
return [self.delegate edgeInsetsInCustomWaterflow:self];
}else{
return CustomDefaultEdgeInsets;
}
}
-(NSInteger)colCount{
if ([self.delegate respondsToSelector:@selector(colCountInCustomWaterflow:)]) {
return [self.delegate colCountInCustomWaterflow:self];
}else{
return CustomDefaultColumnCount;
}
}
-(CGFloat)colMargin{
if ([self.delegate respondsToSelector:@selector(colMarginInCustomWaterflow:)]) {
return [self.delegate colMarginInCustomWaterflow:self];
}else{
return CustomDefaultColMargin;
}
}
-(CGFloat)rowMargin{
if ([self.delegate respondsToSelector:@selector(rowMarginInCustomWaterflow:)]) {
return [self.delegate rowMarginInCustomWaterflow:self];
}else{
return CustomDefaultRowMargin;
}
}
//懒加载
-(NSMutableArray *)colHeightArray{
if (!_colHeightArray) {
_colHeightArray = [NSMutableArray array];
}
return _colHeightArray;
}
-(NSMutableArray *)attributesAarray{
//创建一个数组(存放所有cell的布局属性)
if (!_attributesAarray) {
_attributesAarray = [NSMutableArray array];
}
return _attributesAarray;
}
//初始化
-(void)prepareLayout{
[super prepareLayout];
self.contentHeight = 0;
//清除以前计算的所有高度
[self.colHeightArray removeAllObjects];
for (NSInteger i = 0; i < self.colCount; i++) {
[self.colHeightArray addObject:@(self.edgeInsets.top)];
}
//清空之前所有的布局属性
[self.attributesAarray removeAllObjects];
//开始创建每一个cell对应的布局属性
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < count; i++) {
//创建位置
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//获取indexPath位置cell的对应的布局属性
UICollectionViewLayoutAttributes * attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attributesAarray addObject:attrs];
}
}
//决定cell的排布
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.attributesAarray;
}
//返回indexPath位置cell对应的布局属性
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
//创建布局属性
UICollectionViewLayoutAttributes * attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//collectionView的宽度
CGFloat collectionWidth = self.collectionView.frame.size.width;
//设置布局属性的frame
CGFloat w = (collectionWidth - self.edgeInsets.left - self.edgeInsets.right - (self.colCount - 1) * self.colMargin) / self.colCount;
CGFloat h = [self.delegate customWaterflow:self heightForItemAtIndexPath:indexPath.item itemWidth:w];
NSInteger destCol = 0;
CGFloat minColHeight = [self.colHeightArray[0] doubleValue];
for (NSInteger i = 1; i < self.colCount; i++) {
CGFloat colHeight = [self.colHeightArray[i] doubleValue];
if (minColHeight > colHeight) {
minColHeight = colHeight;
destCol = i;
}
}
CGFloat x = self.edgeInsets.left + destCol * (self.colMargin + w);
CGFloat y = minColHeight;
if (y != self.edgeInsets.top) {
//非第一行
y += self.rowMargin;
}
attrs.frame = CGRectMake(x,y,w,h);
//更新最短那列的高度
self.colHeightArray[destCol] = @(CGRectGetMaxY(attrs.frame));
CGFloat colHeight = [self.colHeightArray[destCol] doubleValue];
if (self.contentHeight < colHeight) {
self.contentHeight = colHeight;
}
return attrs;
}
-(CGSize)collectionViewContentSize{
return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
}
@end