什么是瀑布流?
瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格。
实现瀑布流的必要条件:等宽不等高,宽高比!
代码如下:
控制器.m文件
- (void)viewDidLoad {
[super viewDidLoad];
//实例化布局(自定义布局)
WaterFallLayout * waterLayout = [[WaterFallLayout alloc] init];
waterLayout.delegate = self;
//创建collectionView
UICollectionView * collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:waterLayout];
collectionView.backgroundColor = [UIColor whiteColor];
//注册waterFallCell
[collectionView registerClass:[WaterFallCell class] forCellWithReuseIdentifier:ID];
self.collectionView = collectionView;
collectionView.dataSource = self;
[self.view addSubview:collectionView];
}
#pragma mark - UICollectionViewDataSource方法
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
//返回数据的个数
return self.dataArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
WaterFallCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
//设置背景色
cell.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(255.0) / 255.0) green:(arc4random_uniform(255.0) / 255.0) blue:(arc4random_uniform(255.0) / 255.0) alpha:1.0];
return cell;
}
</pre><pre name="code" class="objc">//自定义布局的代理方法
- (CGFloat)fallLayout:(WaterFallLayout *)fallLayout heightForWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath
{
WaterModel * model = self.dataArray[indexPath.item];
return model.h / model.w * width;
}
自定义布局的.h文件
#import <UIKit/UIKit.h>
@class WaterFallLayout;
@protocol WaterFallLayoutDelegate <NSObject>
//代理方法
- (CGFloat)fallLayout:(WaterFallLayout *)fallLayout heightForWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath;
@end
@interface WaterFallLayout : UICollectionViewFlowLayout
/** 列数 */
@property (nonatomic,assign) NSInteger colCount;
<pre name="code" class="objc">/** 内边距 */
@property (assign,nonatomic) UIEdgeInsets sectionInsets;/** 列间距 */@property (nonatomic,assign) CGFloat colMargin;/** 行间距 */@property (nonatomic,assign) CGFloat rowMargin;@property (weak,nonatomic) id<WaterFallLayoutDelegate> delegate;@end
自定义布局的.m文件
#import "WaterFallLayout.h"
@interface WaterFallLayout ()
/** 用来记录每列最小的y值 */
@property (strong,nonatomic) NSMutableArray * array;
/** 存放所有的布局属性 */
@property (strong,nonatomic) NSMutableArray * attrsArray;
@end
@implementation WaterFallLayout
- (NSMutableArray *)array
{
if (_array == nil) {
_array = [NSMutableArray array];
}
return _array;
}
- (NSMutableArray *)attrsArray
{
if (_attrsArray == nil) {
_attrsArray = [NSMutableArray array];
}
return _attrsArray;
}
- (instancetype)init
{
if (self = [super init]) {
self.sectionInsets = UIEdgeInsetsMake(10, 10, 10, 10);
self.rowMargin = 10;
self.colMargin = 10;
self.colCount = 3;
}
return self;
}
/**
* 返回每一个cell的布局属性
*
* @param indexPath cell的下标
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
//cell的宽度
CGFloat cellWidth = (self.collectionView.frame.size.width - self.sectionInsets.left * 2 - (self.colCount - 1) * self.rowMargin) / self.colCount;
//cell的高度
CGFloat cellheight = [self.delegate fallLayout:self heightForWidth:cellWidth indexPath:indexPath];
//假设取数组中第0个元素y值最小
CGFloat minY = [self.array[0] doubleValue];
//最小列
NSInteger minColumn = 0;
for(NSInteger i = 0;i < self.colCount;i ++ ){
CGFloat currentY = [self.array[i] doubleValue];
if (minY > currentY) {
minY = currentY;
minColumn = i;
}
}
//cell的x
CGFloat cellX = self.sectionInsets.left + (cellWidth + self.colMargin) * minColumn;
//cell的y
CGFloat cellY = minY + self.rowMargin;
UICollectionViewLayoutAttributes * attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.frame = CGRectMake(cellX, cellY, cellWidth, cellheight);
//更新最小y值
self.array[minColumn] = @(CGRectGetMaxY(attrs.frame));
NSLog(@"%@",NSStringFromCGRect(attrs.frame));
return attrs;
}
- (void)prepareLayout
{
[super prepareLayout];
for (NSInteger i = 0; i < self.colCount; i ++) {
self.array[i] = @"0";
}
[self.attrsArray removeAllObjects];
NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < cellCount; i ++) {
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes * att = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attrsArray addObject:att];
}
}
//用来确定垂直滚动的范围
- (CGSize)collectionViewContentSize
{
if (!self.array.count) {
return CGSizeZero;
}
CGFloat maxY = [self.array[0] doubleValue];
for (NSInteger i = 0 ; i < self.colCount; i ++) {
CGFloat currentY = [self.array[i] doubleValue];
if (maxY < currentY) {
maxY = currentY;
}
}
return CGSizeMake(0, maxY + self.sectionInsets.bottom);
}
/**
* 返会范围内的cell布局属性
*
* @param rect 范围
*/
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
//更新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
@end