效果图
实现思路
UICollectionView的精髓就是UICollectionViewLayout。UICollectionViewLayout决定了UICollectionView是如何显示在界面上的。因此我们需要自定义一个UICollectionViewLayout 的子类,在子类里面重写生成布局的方法,创建我们自己需要的布局
实现原理
重写- (void)prepareLayout进行提前创建布局
重写- (CGSize)collectionViewContentSize返回内容的大小
重写 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect方法返回rect中所有元素的布局属性,返回的是一个数组
重写 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath方法返回对应的indexPath的位置的cell的布局属性。
重写 - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的追加视图的布局属性,如果没有就不用重载
重写 - (nullable UICollectionViewLayoutAttributes )layoutAttributesForDecorationViewOfKind:(NSString)elementKind atIndexPath:(NSIndexPath *)indexPath;方法返回对应indexPath的位置的装饰视图的布局属性,如果没有也不需要重载
重写 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;当边界发生改变时,是否应该刷新。
注意:其中
(void)prepareLayout
(CGSize)collectionViewContentSize
(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
这三个方法是必须要重写的,后面的方法可以选择性重写,因为最终collectionView 还是根据 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect 方法来进行展示布局的,
而重写 prepareLayout是因为创建布局的时机 需要提前到 prepareLayout 里面。
而collectionViewContentSize 是返回内容宽和高的,也必须重写
核心计算逻辑
每次都获取到总高度最低的一列cell,在该列添加新的布局
实现代码
协议文件
//
// LBWaterFallLayoutProtocol.h
// Pods
//
// Created by Apple on 2021/9/24.
//
#ifndef LBWaterFallLayoutProtocol_h
#define LBWaterFallLayoutProtocol_h
@protocol LBWaterFallLayoutDelegate<NSObject>
@optional
/// 每个区多少列
- (NSInteger)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout columnNumberAtSection:(NSInteger )section;
/// cell size
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
/// header size
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
/// footer size
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
/// 每个区的边距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
/// 每个区内部的垂直距离
- (CGFloat)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
/// 每个区内部的水平距离
- (CGFloat)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
///使用配置的contentSize,和 customContentSize方法配合使用
/// 这时候使用代理方法配置的contentSize
- (BOOL)useCustomContentSize;
/// 配置的contentSize
- (CGSize)customContentSize;
/// 装饰视图的类名数组
- (NSArray <Class>*)decorationViewClasses;
/// 根据indexPath获取装饰视图的类
- (Class)decorationViewClassOfIndexPath:(NSIndexPath *)indexPath;
@end
@protocol LBWaterFallLayoutProtocol<NSObject,
LBWaterFallLayoutDelegate>
@property (nonatomic, weak) id<LBWaterFallLayoutDelegate> lb_layoutDelegate;
@end
#endif /* LBWaterFallLayoutProtocol_h */
.h 文件
//
// LBWaterFallLayout.h
// LBWaterFallLayout
//
// Created by Apple on 2021/9/25.
//
#import <UIKit/UIKit.h>
#import "LBWaterFallLayoutProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface LBWaterFallLayout : UICollectionViewFlowLayout<LBWaterFallLayoutProtocol>
@property (nonatomic, assign) CGFloat minimumLineSpacing; // default 0.0
@property (nonatomic, assign) CGFloat minimumInteritemSpacing; // default 0.0
@property (nonatomic, assign) BOOL sectionHeadersPinToVisibleBounds; // default NO
@property (nonatomic, assign) CGFloat contentOffsetY;
/// 未悬停的header的布局属性
- (UICollectionViewLayoutAttributes *)originLayoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
/// 适用于每一列的cell宽度都相等
- (CGFloat)itemWidthOfIndexPath:(NSIndexPath *)indexPath;
@end
NS_ASSUME_NONNULL_END
.m 文件
//
// LBWaterFallLayout.m
// LBWaterFallLayout
//
// Created by Apple on 2021/9/25.
//
#import "LBWaterFallLayout.h"
@interface LBCollectionLayoutSectionModel : NSObject
@property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *headerLayoutAttributes;
@property (nonatomic, strong, nullable) NSMutableArray<UICollectionViewLayoutAttributes *> *itemLayoutAttributes_list;
@property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *footerLayoutAttributes;
@end
@implementation LBCollectionLayoutSectionModel
- (instancetype)init
{
self = [super init];
if (self) {
self.itemLayoutAttributes_list = [NSMutableArray new];
}
return self;
}
@end
@interface LBWaterFallLayout ()
@property (nonatomic, strong) NSMutableArray<NSMutableArray<UICollectionViewLayoutAttributes *> *> *itemLayoutAttributes;
@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *originHeaderLayoutAttributes;
@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *headerLayoutAttributes;
@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *originFooterLayoutAttributes;
@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *footerLayoutAttributes;
/// Per section heights.
@property (nonatomic, strong) NSMutableArray<NSNumber *> *heightOfSections;
/// UICollectionView content height.
@property (nonatomic, assign) CGFloat contentHeight;
@property (nonatomic, strong) NSMutableArray <LBCollectionLayoutSectionModel *>* sectionLayoutModels;
@end
@implementation LBWaterFallLayout
@synthesize lb_layoutDelegate;
- (void)prepareLayout
{
[super prepareLayout];
[self vv_registerDecorationViews];
self.contentHeight = 0.0;
@autoreleasepool {
self.itemLayoutAttributes = nil;
self.headerLayoutAttributes = nil;
self.footerLayoutAttributes = nil;
self.heightOfSections = nil;
self.sectionLayoutModels = nil;
}
self.itemLayoutAttributes = [NSMutableArray array];
self.headerLayoutAttributes = [NSMutableArray array];
self.footerLayoutAttributes = [NSMutableArray array];
self.originHeaderLayoutAttributes = [NSMutableArray array];
self.originFooterLayoutAttributes = [NSMutableArray array];
self.heightOfSections = [NSMutableArray array];
self.sectionLayoutModels = [NSMutableArray new];
[self invalidateLayout];
UICollectionView *collectionView = self.collectionView;
NSInteger const numberOfSections = collectionView.numberOfSections;
for (NSInteger section = 0; section < numberOfSections; section++) {
LBCollectionLayoutSectionModel *sectionLayoutModel = [LBCollectionLayoutSectionModel new];
CGFloat headerHeight = [self layoutHeaderSection:section sectionLayoutModel:sectionLayoutModel];
NSInteger columnOfSection = 1;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:columnNumberAtSection:)]) {
columnOfSection = [self.lb_layoutDelegate collectionView:collectionView customLayout:self columnNumberAtSection:section];
} else {
#if DEBUG
NSAssert(NO, @"未设置列数");
#endif
}
CGFloat offsetOfColumns[columnOfSection];
[self layoutItemSection:section headerHeight:headerHeight offsetOfColumns:offsetOfColumns columnOfSection:columnOfSection sectionLayoutModel:sectionLayoutModel];
UIEdgeInsets const contentInsetOfSection = [self contentInsetForSection:section];
CGFloat maxOffsetValue = offsetOfColumns[0];
for (int i = 1; i < columnOfSection; i++) {
if (offsetOfColumns[i] > maxOffsetValue) {
maxOffsetValue = offsetOfColumns[i];
}
}
maxOffsetValue += contentInsetOfSection.bottom;
CGFloat footerHeight = [self layoutFooterSection:section maxOffsetValue:maxOffsetValue sectionLayoutModel:sectionLayoutModel];
CGFloat currentSectionHeight = maxOffsetValue + footerHeight;
[self.heightOfSections addObject:@(currentSectionHeight)];
self.contentHeight += currentSectionHeight;
[self.sectionLayoutModels addObject:sectionLayoutModel];
}
}
- (CGFloat)layoutHeaderSection:(NSInteger)section sectionLayoutModel:(LBCollectionLayoutSectionModel *)sectionLayoutModel
{
UICollectionView *collectionView = self.collectionView;
UIEdgeInsets const contentInset = collectionView.contentInset;
CGFloat const contentWidth = collectionView.bounds.size.width - contentInset.left - contentInset.right;
CGFloat headerHeight = 0.0;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:referenceSizeForHeaderInSection:)]) {
CGSize headerSize = [self.lb_layoutDelegate collectionView:collectionView customLayout:self referenceSizeForHeaderInSection:section];
headerHeight = headerSize.height;
}
UICollectionViewLayoutAttributes *headerLayoutAttribute = [[UICollectionViewLayoutAttributes alloc] init];
headerLayoutAttribute.indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
if (headerHeight > 0) {
headerLayoutAttribute = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
}
headerLayoutAttribute.frame = CGRectMake(0.0, self.contentHeight, contentWidth, headerHeight);
[self.headerLayoutAttributes addObject:headerLayoutAttribute];
[self.originHeaderLayoutAttributes addObject:[headerLayoutAttribute copy]];
sectionLayoutModel.headerLayoutAttributes = [headerLayoutAttribute copy];
return headerHeight;
}
- (void)layoutItemSection:(NSInteger)section
headerHeight:(CGFloat)headerHeight
offsetOfColumns:(CGFloat [])offsetOfColumns
columnOfSection:(NSInteger)columnOfSection
sectionLayoutModel:(LBCollectionLayoutSectionModel *)sectionLayoutModel
{
UICollectionView *collectionView = self.collectionView;
UIEdgeInsets const contentInset = collectionView.contentInset;
CGFloat const contentWidth = collectionView.bounds.size.width - contentInset.left - contentInset.right;
UIEdgeInsets const contentInsetOfSection = [self contentInsetForSection:section];
CGFloat const minimumLineSpacing = [self minimumLineSpacingForSection:section];
CGFloat const minimumInteritemSpacing = [self minimumInteritemSpacingForSection:section];
CGFloat const contentWidthOfSection = contentWidth - contentInsetOfSection.left - contentInsetOfSection.right;
CGFloat const itemWidth = (contentWidthOfSection - (columnOfSection - 1) * minimumInteritemSpacing) / columnOfSection;
NSInteger numberOfItems = 0;
if ([collectionView.dataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]) {
numberOfItems = [collectionView.dataSource collectionView:collectionView numberOfItemsInSection:section];
}
for (NSInteger i = 0; i < columnOfSection; i++) {
offsetOfColumns[i] = headerHeight + contentInsetOfSection.top;
}
NSMutableArray *layoutAttributeOfSection = [NSMutableArray arrayWithCapacity:numberOfItems];
for (NSInteger item = 0; item < numberOfItems; item++) {
NSInteger currentColumn = 0;
for (NSInteger i = 1; i < columnOfSection; i++) {
if (offsetOfColumns[currentColumn] > offsetOfColumns[i]) {
currentColumn = i;
}
}
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
CGFloat itemHeight = 0;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:sizeForItemAtIndexPath:)]) {
CGSize itemSize = [self.lb_layoutDelegate collectionView:collectionView customLayout:self sizeForItemAtIndexPath:indexPath];
itemHeight = itemSize.height;
}
CGFloat x = contentInsetOfSection.left + itemWidth*currentColumn + minimumInteritemSpacing * currentColumn;
CGFloat y = offsetOfColumns[currentColumn] + (item >= columnOfSection minimumLineSpacing : 0.0);
UICollectionViewLayoutAttributes *layoutAttbiture = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
layoutAttbiture.frame = CGRectMake(x, y + self.contentHeight, itemWidth, itemHeight);
[layoutAttributeOfSection addObject:layoutAttbiture];
offsetOfColumns[currentColumn] = (y + itemHeight);
}
[self.itemLayoutAttributes addObject:layoutAttributeOfSection];
sectionLayoutModel.itemLayoutAttributes_list = [layoutAttributeOfSection copy];
}
- (CGFloat)layoutFooterSection:(NSInteger)section
maxOffsetValue:(CGFloat)maxOffsetValue
sectionLayoutModel:(LBCollectionLayoutSectionModel *)sectionLayoutModel
{
UICollectionView *collectionView = self.collectionView;
UIEdgeInsets const contentInset = collectionView.contentInset;
CGFloat const contentWidth = collectionView.bounds.size.width - contentInset.left - contentInset.right;
CGFloat footerHeight = 0.0;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:referenceSizeForFooterInSection:)]) {
CGSize footerSize = [self.lb_layoutDelegate collectionView:collectionView customLayout:self referenceSizeForFooterInSection:section];
footerHeight = footerSize.height;
}
UICollectionViewLayoutAttributes *footerLayoutAttribute = [[UICollectionViewLayoutAttributes alloc] init];
footerLayoutAttribute.indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
if (footerHeight > 0) {
footerLayoutAttribute = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
}
footerLayoutAttribute.frame = CGRectMake(0.0, self.contentHeight + maxOffsetValue, contentWidth, footerHeight);
[self.footerLayoutAttributes addObject:footerLayoutAttribute];
[self.originFooterLayoutAttributes addObject:[footerLayoutAttribute copy]];
sectionLayoutModel.footerLayoutAttributes = [footerLayoutAttribute copy];
return footerHeight;
}
- (CGSize)collectionViewContentSize
{
if ([self.lb_layoutDelegate respondsToSelector:@selector(useCustomContentSize)] &&
[self.lb_layoutDelegate respondsToSelector:@selector(customContentSize)] &&
[self.lb_layoutDelegate useCustomContentSize]) {
///使用自定义contentSize,、
return [self.lb_layoutDelegate customContentSize];;
}
UIEdgeInsets contentInset = self.collectionView.contentInset;
CGFloat width = CGRectGetWidth(self.collectionView.bounds) - contentInset.left - contentInset.right;
CGFloat height = MAX(CGRectGetHeight(self.collectionView.bounds), self.contentHeight);
return CGSizeMake(width, height);
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray<UICollectionViewLayoutAttributes *> *result = [NSMutableArray array];
// 悬停
if (self.sectionHeadersPinToVisibleBounds) {
for (UICollectionViewLayoutAttributes *attriture in self.headerLayoutAttributes) {
NSInteger section = attriture.indexPath.section;
UIEdgeInsets contentInsetOfSection = [self contentInsetForSection:section];
NSIndexPath *firstIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
UICollectionViewLayoutAttributes *itemAttribute = [self layoutAttributesForItemAtIndexPath:firstIndexPath];
if (!itemAttribute) {
continue;
}
CGFloat headerHeight = CGRectGetHeight(attriture.frame);
CGRect frame = attriture.frame;
frame.origin.y = MIN(
MAX(self.collectionView.contentOffset.y + self.contentOffsetY, CGRectGetMinY(itemAttribute.frame)-headerHeight-contentInsetOfSection.top),
CGRectGetMinY(attriture.frame)+[self.heightOfSections[section] floatValue]-headerHeight
);
attriture.frame = frame;
attriture.zIndex = (NSIntegerMax/2)+section;
}
}
[self.sectionLayoutModels enumerateObjectsUsingBlock:^(LBCollectionLayoutSectionModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSUInteger section = idx;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
[self mutableArray:result addDecorationViewAttributWithIndexPath:indexPath];
if(obj.headerLayoutAttributes
&& !(obj.headerLayoutAttributes.frame.size.height == 0)
&& CGRectIntersectsRect(rect, obj.headerLayoutAttributes.frame)) {
[result addObject:obj.headerLayoutAttributes];
}
if (obj.itemLayoutAttributes_list.count > 0) {
[obj.itemLayoutAttributes_list enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if(CGRectIntersectsRect(rect, obj.frame)) {
[result addObject:obj];
}
}];
}
if(obj.footerLayoutAttributes
&& !(obj.footerLayoutAttributes.frame.size.height == 0)
&& CGRectIntersectsRect(rect, obj.footerLayoutAttributes.frame)) {
[result addObject:obj.footerLayoutAttributes];
}
}];
return result;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSArray<UICollectionViewLayoutAttributes *> *tempArray = (NSArray<UICollectionViewLayoutAttributes *> *)self.itemLayoutAttributes[indexPath.section];
return tempArray[indexPath.item];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) {
return self.headerLayoutAttributes[indexPath.section];
}
if ([elementKind isEqualToString:UICollectionElementKindSectionFooter]) {
return self.footerLayoutAttributes[indexPath.section];
}
return nil;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
if (self.sectionHeadersPinToVisibleBounds) {
return YES;
} else {
return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes* att = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
CGFloat sectionHeight = [self.heightOfSections[indexPath.section] floatValue];
CGFloat origin_y = [self originYofSection:indexPath.section];
UIEdgeInsets sectionInsets = [self contentInsetForSection:indexPath.section];
att.frame = CGRectMake(0, origin_y + sectionInsets.top, self.collectionView.contentSize.width, sectionHeight -(sectionInsets.top + sectionInsets.bottom));
att.zIndex= -1;
return att;
}
- (UICollectionViewLayoutAttributes *)originLayoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) {
return self.originHeaderLayoutAttributes[indexPath.section];
}
if ([elementKind isEqualToString:UICollectionElementKindSectionFooter]) {
return self.originFooterLayoutAttributes[indexPath.section];
}
return nil;
}
- (CGFloat)itemWidthOfIndexPath:(NSIndexPath *)indexPath
{
UIEdgeInsets contentInset = self.collectionView.contentInset;
CGFloat contentWidth = self.collectionView.bounds.size.width - contentInset.left - contentInset.right;
UIEdgeInsets contentInsetOfSection = [self contentInsetForSection:indexPath.section];
CGFloat minimumInteritemSpacing = [self minimumInteritemSpacingForSection:indexPath.section];
NSInteger columnOfSection = [self columnNumAtSection:indexPath.section];
CGFloat contentWidthOfSection = contentWidth - contentInsetOfSection.left - contentInsetOfSection.right;
CGFloat itemWidth = (contentWidthOfSection - (columnOfSection - 1) * minimumInteritemSpacing) / columnOfSection;
return itemWidth;
}
#pragma mark - Private
- (UIEdgeInsets)contentInsetForSection:(NSInteger)section
{
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:insetForSectionAtIndex:)]) {
edgeInsets = [self.lb_layoutDelegate collectionView:self.collectionView customLayout:self insetForSectionAtIndex:section];
}
return edgeInsets;
}
- (CGFloat)minimumLineSpacingForSection:(NSInteger)section
{
CGFloat minimumLineSpacing = self.minimumLineSpacing;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:minimumLineSpacingForSectionAtIndex:)]) {
minimumLineSpacing = [self.lb_layoutDelegate collectionView:self.collectionView customLayout:self minimumLineSpacingForSectionAtIndex:section];
}
return minimumLineSpacing;
}
- (CGFloat)minimumInteritemSpacingForSection:(NSInteger)section
{
CGFloat minimumInteritemSpacing = self.minimumInteritemSpacing;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:minimumInteritemSpacingForSectionAtIndex:)]) {
minimumInteritemSpacing = [self.lb_layoutDelegate collectionView:self.collectionView customLayout:self minimumInteritemSpacingForSectionAtIndex:section];
}
return minimumInteritemSpacing;
}
- (NSInteger)columnNumAtSection:(NSInteger)section
{
NSInteger columnOfSection = 1;
if ([self.lb_layoutDelegate respondsToSelector:@selector(collectionView:customLayout:columnNumberAtSection:)]) {
columnOfSection = [self.lb_layoutDelegate collectionView:self.collectionView customLayout:self columnNumberAtSection:section];
return columnOfSection;
} else {
#if DEBUG
NSAssert(NO, @"未设置列数");
#endif
}
return 1;
}
- (void)vv_registerDecorationViews
{
if (self.lb_layoutDelegate
&& [self.lb_layoutDelegate respondsToSelector:@selector(decorationViewClasses)]) {
NSArray <Class>*classes = [self.lb_layoutDelegate decorationViewClasses];
for (Class decorationViewClass in classes) {
if ([decorationViewClass isSubclassOfClass:[UICollectionReusableView class]]) {
[self registerClass:decorationViewClass forDecorationViewOfKind:NSStringFromClass([decorationViewClass class])];
}
}
}
}
- (void)mutableArray:(NSMutableArray *)results addDecorationViewAttributWithIndexPath:(NSIndexPath *)indexPath
{
if (self.lb_layoutDelegate
&& [self.lb_layoutDelegate respondsToSelector:@selector(decorationViewClassOfIndexPath:)]) {
Class decorationViewClass = [self.lb_layoutDelegate decorationViewClassOfIndexPath:indexPath];
if ([decorationViewClass isSubclassOfClass:[UICollectionReusableView class]]) {
[results addObject:[self layoutAttributesForDecorationViewOfKind: NSStringFromClass([decorationViewClass class]) atIndexPath:indexPath]];
}
}
}
- (CGFloat)originYofSection:(NSUInteger)section
{
CGFloat origin_y = 0;
for (NSUInteger index = 0; index < section; index++) {
CGFloat sectionHeight = [self.heightOfSections[index] floatValue];
origin_y += sectionHeight;
}
return origin_y;
}
@end
使用方法
pod 'LBWaterFallLayout'
///初始化,并设置代理
- (UICollectionView *)collectionView
{
if (!_collectionView) {
LBWaterFallLayout *layout = [[LBWaterFallLayout alloc] init];
layout.lb_layoutDelegate = self;
_collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
if (@available(iOS 10.0, *)) {
_collectionView.prefetchingEnabled = NO;
} else {
// Fallback on earlier versions
}
_collectionView.scrollEnabled = YES;
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
if (@available(iOS 11.0, *)) {
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
// Fallback on earlier versions
}
_collectionView.backgroundColor = [UIColor redColor];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([UICollectionViewCell class])];
}
return _collectionView;
}
/// 实现代理方法
#pragma mark - VVCollectionCustomLayoutDelegate
/// 每个区多少列
- (NSInteger)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout columnNumberAtSection:(NSInteger )section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return layout.colomnsNum;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return layout.sectionEdgeInsets;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return layout.lineSpace;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return layout.itemSpace;
}
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return CGSizeMake(collectionView.contentSize.width, layout.headerHeight);
}
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
LBSectionConfigModel *layout = self.sectionConfigArray[section];
return CGSizeMake(collectionView.contentSize.width, layout.footerHeight);
}
- (CGSize)collectionView:(UICollectionView *)collectionView customLayout:(LBWaterFallLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSString *title = self.dataArray[indexPath.item % self.dataArray.count];
CGFloat height = [title boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.bounds)/2 - 30, 200) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20]} context:nil].size.height;
return CGSizeMake(CGRectGetWidth(self.view.bounds)/2 - 30, height + 150);
//return CGSizeMake(, 100);
}