创建瀑布流有三种方式:
第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可。这种方式太奇葩,不太推荐使用...
第二种方式:在一个ScrollView里面从左到右依次放入三个UIView,当ScrollView滚动时,如果之前的三个view消失就将它们存入自定义的缓冲池,即数组中,下拉时再从数组中取出这三个view放到之前三个view位置的下面。但是,切记,每次要依次计算补全最短的,防止拉开的间距越来越大...,这种方式稍微麻烦一些…
第三种方式:其实就是上面第二种的简单版,就是使用UICollectionView,因为它有自己的重用机制,我们只需要做的就是计算下一次cell的位置即可....
集合视图的自动布局:UICollectionViewLayout是抽象根类,必须用它的子类才能创建实例,下面是重写的方法,计算item的布局属性
//每一次重新布局前,都会准备布局(苹果官方推荐使用该方法进行一些初始化)
-(void)prepareLayout
//重写layoutAttributesForItemAtIndexPath,返回每一个item的布局属性(流式布局内部已经帮助完成)
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
//是否要重新刷新布局(只要显示的item边界发生改变就重新布局)
//只要每一次重新布局内部就会调用下面的layoutAttributesForElementsInRect:获取所有cell(item)的属性
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
//返回需要重新布局的所有item属性
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
?:下面的瀑布流布局可以直接拿走使用
下面使用第三种方式创建的瀑布流代码如下:
WaterFlowLayout.h
#import <UIKit/UIKit.h>
/*
为了体现封装性的特点,我们可以把一些数据设置为公共的,既可以提高扩展性和通用性,
也便于外界按照自己的需求做必要的调整。
*/
@protocol WaterFlowLayoutDelegate; //设置代理传递数据,降低了与其他类的耦合性,通用性更强
@class WaterFlowLayout;
@interface WaterFlowLayout : UICollectionViewLayout
@property (assign,nonatomic)CGFloat columnMargin;//每一列item之间的间距
@property (assign,nonatomic)CGFloat rowMargin; //每一行item之间的间距
@property (assign,nonatomic)UIEdgeInsets sectionInset;//设置于collectionView边缘的间距
@property (assign,nonatomic)NSInteger columnCount;//设置每一行排列的个数
@property (weak,nonatomic)id<WaterFlowLayoutDelegate> delegate; //设置代理
@end
@protocol WaterFlowLayoutDelegate
-(CGFloat)waterFlowLayout:(WaterFlowLayout *) WaterFlowLayout heightForWidth:(CGFloat)width andIndexPath:(NSIndexPath *)indexPath;
@end
WaterFloeLayout.m
#import "WaterFlowLayout.h"
//每一列item之间的间距
//static const CGFloat columnMargin = 10;
//每一行item之间的间距
//static const CGFloat rowMargin = 10;
@interface WaterFlowLayout()
/** 这个字典用来存储每一列item的高度 */
@property (strong,nonatomic)NSMutableDictionary *maxYDic;
/** 存放每一个item的布局属性 */
@property (strong,nonatomic)NSMutableArray *attrsArray;
@end
@implementation WaterFlowLayout
/** 懒加载 */
-(NSMutableDictionary *)maxYDic
{
if (!_maxYDic)
{
_maxYDic = [NSMutableDictionary dictionary];
}
return _maxYDic;
}
/** 懒加载 */
-(NSMutableArray *)attrsArray
{
if (!_attrsArray)
{
_attrsArray = [NSMutableArray array];
}
return _attrsArray;
}
//初始化
-(instancetype)init
{
if (self = [super init]){
self.columnMargin = 10;
self.rowMargin = 10;
self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
self.columnCount = 3;
}
return self;
}
//每一次布局前的准备工作
-(void)prepareLayout
{
[super prepareLayout];
//清空最大的y值
for (int i =0; i < self.columnCount; i++)
{
NSString *column = [NSString stringWithFormat:@"%d",i];
self.maxYDic[column] = @(self.sectionInset.top);
}
//计算所有item的属性
[self.attrsArray removeAllObjects];
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i=0; i<count; i++)
{
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attrsArray addObject:attrs];
}
}
//设置collectionView滚动区域
-(CGSize)collectionViewContentSize
{
//假设最长的那一列为第0列
__block NSString *maxColumn = @"0";
//遍历字典,找出最长的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] > [self.maxYDic[maxColumn] floatValue])
{
maxColumn = column;
}
}];
return CGSizeMake(0, [self.maxYDic[maxColumn]floatValue]+self.sectionInset.bottom);
}
//允许每一次重新布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
//布局每一个属性
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
//假设最短的那一列为第0列
__block NSString *minColumn = @"0";
//遍历字典,找出最短的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] < [self.maxYDic[minColumn] floatValue])
{
minColumn = column;
}
}];
//计算每一个item的宽度和高度
CGFloat width = (self.collectionView.frame.size.width - self.columnMargin*(self.columnCount - 1) - self.sectionInset.left - self.sectionInset.right) / self.columnCount;
CGFloat height = [self.delegate waterFlowLayout:self heightForWidth:width andIndexPath:indexPath] ;
//计算每一个item的位置
CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn floatValue];
CGFloat y = [self.maxYDic[minColumn] floatValue] + self.rowMargin;
//更新这一列的y值
self.maxYDic[minColumn] = @(y + height);
//创建布局属性
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//设置item的frame
attrs.frame = CGRectMake(x, y, width, height);
return attrs;
}
//布局所有item的属性,包括header、footer
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
@end
瀑布流演示结果如下: