我是照着 MJ 的视频敲得.谢谢 MJ老师
其中,每个方法的注释,有一个数字,表示每一个方法的调用顺序
#import <UIKit/UIKit.h>
@class WaterFlowLayout;
@protocol WaterFlowLayoutDelegate <NSObject>
- (CGFloat)waterflowLayout:(WaterFlowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;
@end
@interface WaterFlowLayout : UICollectionViewLayout
@property (nonatomic , assign) UIEdgeInsets sectionInset;
/** 每一列的间距 */
@property (nonatomic , assign) CGFloat columnMargin;
/** 每一行的间距 */
@property (nonatomic , assign) CGFloat rowMargin;
/** 显示多少列 */
@property (nonatomic , assign) int columnsCount;
@property (nonatomic , weak) id<WaterFlowLayoutDelegate> delegate;
@end
#import "WaterFlowLayout.h"
@interface WaterFlowLayout ()
/** 这个字典用来存储每一列最大的 Y值 */
@property (nonatomic , strong) NSMutableDictionary *maxYDict;
/** 存放所有的布局属性 */
@property (nonatomic , strong) NSMutableArray *attrsArray;
@end
@implementation WaterFlowLayout
- (NSMutableDictionary *)maxYDict
{
if (!_maxYDict) {
_maxYDict = [[NSMutableDictionary alloc] init];
}
return _maxYDict;
}
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
_attrsArray = [NSMutableArray array];
}
return _attrsArray;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.rowMargin = 10;
self.columnMargin = 10;
self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
/** 默认显示 */
self.columnsCount = 3;
}
return self;
}
/** 每一次滑动的时候调用 */
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
// 1.每次布局之前的准备
- (void)prepareLayout
{
[super prepareLayout];
// 1.清空最大的 Y 值
for (int i = 0; i < self.columnsCount; i++) {
NSString *column = [NSString stringWithFormat:@"%d" , i];
self.maxYDict[column] = @(self.sectionInset.top);
}
// 2.假设所有 cell 的属性
[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];
}
}
/**
* 3.返回所有的尺寸
* 会多次调用
*/
- (CGSize)collectionViewContentSize
{
__block NSString *maxColumn = @"0";
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] > [self.maxYDict[maxColumn] floatValue]) {
maxColumn = column;
}
}];
return CGSizeMake(0, [self.maxYDict[maxColumn] floatValue] + self.sectionInset.bottom);
}
/**
* 2.返回 indexPath 这个位置 item 的布局属性
* 会多次调用
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
// 假设最短的那一列是第0列
__block NSString *minColumn = @"0";
[self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
if ([maxY floatValue] < [self.maxYDict[minColumn] floatValue]) {
minColumn = column;
}
}];
// 计算宽度
CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - (self.columnsCount - 1) * self.columnMargin) / self.columnsCount;
CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];
// 计算位置
CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn intValue];
CGFloat y = [self.maxYDict[minColumn] floatValue] + self.rowMargin;
// 更新这一列的最大 Y值
self.maxYDict[minColumn] = @(y + height);
// 创建属性
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.frame = CGRectMake(x, y, width, height);
return attrs;
}
// 4.返回 rect 范围内的布局属性
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
@end