瀑布流
TTWaterfallFlowLayout.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class TTWaterfallFlowLayout;
@protocol TTWaterfallFlowLayoutDelegate <NSObject>
@required
- (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
@optional
- (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath;
- (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForFooterAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout numForColumnsInSection:(NSInteger )section;
@end
@interface TTWaterfallFlowLayout : UICollectionViewFlowLayout
/// 左边距 默认10
@property (nonatomic, assign) CGFloat leftMargin;
/// 右边距 默认10
@property (nonatomic, assign) CGFloat rightMargin;
/// 行间距
@property (nonatomic, assign) CGFloat rowMargin;
/// 列边距
@property (nonatomic, assign) CGFloat columnsMargin;
/// 列数 默认2列 ==
@property (nonatomic, assign) NSInteger columns;
@property (nonatomic, weak) id<TTWaterfallFlowLayoutDelegate>delegate;
@end
NS_ASSUME_NONNULL_END
TTWaterfallFlowLayout.m
#import "TTWaterfallFlowLayout.h"
@interface TTWaterfallFlowLayout ()
@property (nonatomic, strong) NSMutableArray *attributeArray;
@property (nonatomic, strong) NSMutableArray *lowestYArr;
@end
@implementation TTWaterfallFlowLayout
- (instancetype)init {
self = [super init];
if (self) {
self.lowestYArr = [NSMutableArray arrayWithArray:@[@(0),@(0)]];
self.leftMargin =10;
self.rightMargin =10;
self.columnsMargin =10;
self.rowMargin =10;
self.columns = 2;
}
return self;
}
- (void)prepareLayout {
//清除历史布局
self.attributeArray = [NSMutableArray array];
[self.lowestYArr removeAllObjects];
for (int i=0; i<self.columns; i++) {
[self.lowestYArr addObject:@(0)];
}
[super prepareLayout];
//开始创建每一组cell的布局属性
NSInteger sectionCount = [self.collectionView numberOfSections];
for(NSInteger section = 0; section < sectionCount; section++) {
//头视图
if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForHeaderAtIndexPath:)]) {
UICollectionViewLayoutAttributes *headerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
[self.attributeArray addObject:headerAttrs];
}
//cell
NSInteger rowCount = [self.collectionView numberOfItemsInSection:section];
for (NSInteger row = 0; row < rowCount; row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:section];
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attributeArray addObject:attrs];
}
//尾视图
if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForFooterAtIndexPath:)]) {
UICollectionViewLayoutAttributes *footerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
[self.attributeArray addObject:footerAttrs];
}
}
}
//这个方法中返回我们的布局数组
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.attributeArray;
}
//返回cell的布局
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
//获取列数,根据列数改变
if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:numForColumnsInSection:)]) {
self.columns = [self.delegate waterfallFlowLayout:self numForColumnsInSection:indexPath.section];
}
UICollectionViewLayoutAttributes *attri = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//计算
attri.frame = [self itemFrameOfVerticalWaterfallFlow:indexPath];
return attri;
}
//返回头尾视图的布局
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attri;
if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) { //头视图
attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
CGFloat h = 0;
if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForHeaderAtIndexPath:)]) {
h = [self.delegate waterfallFlowLayout:self sizeForHeaderAtIndexPath:indexPath].height;
}
attri.frame = CGRectMake(0, [self getHighestYInArr:self.lowestYArr], SCREEN_WIDTH, h);
}else { //脚视图
attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];
CGFloat h = 0;
if ([self.delegate respondsToSelector:@selector(waterfallFlowLayout:sizeForFooterAtIndexPath:)]) {
h = [self.delegate waterfallFlowLayout:self sizeForHeaderAtIndexPath:indexPath].height;
}
attri.frame = CGRectMake(0, [self getHighestYInArr:self.lowestYArr], SCREEN_WIDTH, h);
}
// 更新Y数据
CGFloat highest = [self getHighestYInArr:self.lowestYArr] +attri.bounds.size.height;
NSInteger count = self.lowestYArr.count;
[self.lowestYArr removeAllObjects];
for (int i=0; i<count; i++) {
[self.lowestYArr addObject:@(highest)];
}
return attri;
}
- (CGSize)collectionViewContentSize {
return CGSizeMake(SCREEN_WIDTH, [self getHighestYInArr:self.lowestYArr]+20);
}
//竖向瀑布流 item等宽不等高
- (CGRect)itemFrameOfVerticalWaterfallFlow:(NSIndexPath *)indexPath{
//设置布局属性item的frame
//宽度根据左边距、右边距、列边距、列数自动计算
CGFloat h = [self.delegate waterfallFlowLayout:self sizeForItemAtIndexPath:indexPath].height;
CGFloat w = (SCREEN_WIDTH-self.leftMargin-self.rightMargin-(self.columns-1)*self.columnsMargin)/self.columns;
NSInteger index = [self getLowestYIndexInArr:self.lowestYArr];
CGFloat x = self.leftMargin+(w+self.columnsMargin)*index;
CGFloat y = [self.lowestYArr[[self getLowestYIndexInArr:self.lowestYArr]] floatValue];
[self.lowestYArr replaceObjectAtIndex:index withObject:@(y+h+self.rowMargin)];
return CGRectMake(x, y, w, h);
}
#pragma mark -- GET
// 获取相对最高的index
- (NSInteger)getLowestYIndexInArr:(NSMutableArray <NSNumber *>*)arr {
NSInteger index = 0;
CGFloat min = [arr[0] floatValue];
for (int i=0; i<arr.count; i++) {
if ([arr[i] floatValue] < min) {
min = [arr[i] floatValue];
index = i;
}
}
return index;
}
// 获取最低的位置
- (CGFloat)getHighestYInArr:(NSMutableArray <NSNumber *>*)arr {
CGFloat max = [arr[0] floatValue];
for (int i=0; i<arr.count; i++) {
if ([arr[i] floatValue] > max) {
max = [arr[i] floatValue];
}
}
return max;
}
#pragma mark -- SET
- (void)setColumns:(NSInteger)columns {
if (_columns != columns) { //列数更改后需要重新设置y数据
//需重新设置位置
CGFloat highest = [self getHighestYInArr:self.lowestYArr];
[self.lowestYArr removeAllObjects];
for (int i=0; i<columns; i++) {
[self.lowestYArr addObject:@(highest)];
}
}
_columns = columns;
}
@end
使用
TTWaterfallFlowLayout *flowLayout = [TTWaterfallFlowLayout new];
flowLayout.delegate = self;
同系统创建flowLayout,设置代理
实现代理方法
#pragma mark -- TTWaterfallFlowLayoutDelegate
- (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath {
// 代码里写死为屏幕宽度,这里宽度无用到
return CGSizeMake(0,150);
}
- (CGSize)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
// 因为只实现了等宽不等高的一种情况。所以宽度是自动根据间距自动算出来的。这里宽度无效,可自行扩展
if (indexPath.section == 0) {
return CGSizeMake(0, 60);
}else if (indexPath.section == 1) {
return CGSizeMake(0, 160);
}
return CGSizeMake(0, 200+arc4random()%200);
}
- (NSInteger)waterfallFlowLayout:(TTWaterfallFlowLayout *)flowLayout numForColumnsInSection:(NSInteger)section {
if (section == 0) {
return 5;
}else if (section == 1) {
return 3;
}
return 2;
}
代码里用到了部分自定义宏,自行更改即可
网友评论