首先查看效果图
是支持多分区的
思路就是和竖直方向上的瀑布流布局是一样的,
只不过这里记录的列是水平方向的,同时要记录下
当前最小的x 所在的列,其实原理和竖直方向上的是相同的
,下面贴出代码
父类layout中的代码
//
// LBCollectionViewBaseLayout.m
// TEXT
//
// Created by mac on 2024/5/12.
// Copyright © 2024 刘博. All rights reserved.
//
#import "LBCollectionViewBaseLayout.h"
#import "LBCellFakeView.h"
#import "LBCollectionViewLayoutAttributes.h"
typedef NS_ENUM(NSUInteger, LBScrollDirection) {
LBScrollDirectionStay, //不滚动
LBScrollDirectionTop, //滚动到顶部
LBScrollDirectionBottom, //滚动到底部
};
@interface LBCollectionViewBaseLayout () <UIGestureRecognizerDelegate>
//关于拖动的参数
@property (nonatomic, strong) LBCellFakeView *cellFakeView;
@property (nonatomic, strong) UILongPressGestureRecognizer *longPress;
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property (nonatomic, assign) CGPoint fakeCellCenter;
@property (nonatomic, assign) CGPoint panTranslation;
@property (nonatomic, assign) LBScrollDirection continousScrollDirection;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation LBCollectionViewBaseLayout
{
BOOL _isNeedReCalculateAllLayout;
}
- (instancetype)init
{
if (self = [super init]) {
self.isFloor = YES;
self.canDrag = NO;
self.header_suppension = NO;
self.layoutType = LBLayoutTypeTypeFillLayout;
self.columnCount = 1;
self.columnSortType = LBColumnSortTypeMinHeight;
self.fixTop = 0;
self.xBeyong = 3;
_isNeedReCalculateAllLayout = YES;
_headerAttributesArray = [NSMutableArray array];
[self addObserver:self forKeyPath:@"collectionView" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
#pragma mark - 当尺寸有所变化时, 重新刷新
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return self.header_suppension;
}
- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context
{
/***
外部掉用reloadData 或者变更任意数据时则认为需要进行全量布局的刷新
好处时候在外部变量变更数据时内部布局会及时刷新
劣势是在你上拉加载某一页时,布局会全部整体重新计算一遍,并非只计算新增的布局
*/
_isNeedReCalculateAllLayout = context.invalidateEverything ||
context.invalidateDataSourceCounts;
[super invalidateLayoutWithContext:context];
}
//注册所有的背景view(传入类名)
- (void)registerDecorationView:(NSArray<NSString *> *)classNames
{
for (NSString *className in classNames) {
if (className.length > 0) {
[self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
}
}
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:@"collectionView"];
}
#pragma mark - 所有cell和view 的布局属性
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
if (!self.attributesArray ||
self.collectionView.numberOfSections == 0) {
return [super layoutAttributesForElementsInRect:rect];
}
if (self.header_suppension) {
//只有在headerAttributesArray 里面查找需要悬浮的属性
for (UICollectionViewLayoutAttributes *attribute in self.headerAttributesArray) {
if (![attribute.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
continue;
}
NSInteger section = attribute.indexPath.section;
CGRect frame = attribute.frame;
BOOL isNeedChangeFrame = NO;
if (section == 0) {
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
if (offsetY > 0 && offsetY < [self.collectionHeightsArray[0] floatValue]) {
frame.origin.y = offsetY;
attribute.zIndex = 1000 + section;
attribute.frame = frame;
isNeedChangeFrame = YES;
}
} else {
CGFloat offsetX = self.collectionView.contentOffset.x + self.fixTop;
if (offsetX > 0 && offsetX < [self.collectionHeightsArray[0] floatValue]) {
frame.origin.x = offsetX;
attribute.zIndex = 1000 + section;
attribute.frame = frame;
isNeedChangeFrame = YES;
}
}
} else {
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
if (offsetY > [self.collectionHeightsArray[section - 1] floatValue] &&
offsetY < [self.collectionHeightsArray[section] floatValue]) {
frame.origin.y = offsetY;
attribute.zIndex = 1000 + section;
attribute.frame = frame;
isNeedChangeFrame = YES;
}
} else {
CGFloat offsetX = self.collectionView.contentOffset.x + self.fixTop;
if (offsetX > [self.collectionHeightsArray[section - 1] floatValue] &&
offsetX < [self.collectionHeightsArray[section] floatValue]) {
frame.origin.x = offsetX;
attribute.zIndex = 1000 + section;
attribute.frame = frame;
isNeedChangeFrame = YES;
}
}
}
if (isNeedChangeFrame) {
/**
这里需要注意,在悬浮情况下,改变了headeatt的frame,
在滑出header又滑回来时,headeAttr已经被修改过,需要改回原始值,
否则header无法正确回归
*/
if ([attribute isKindOfClass:[LBCollectionViewLayoutAttributes class]]) {
attribute.frame = ((LBCollectionViewLayoutAttributes *)attribute).originalFrame;
}
}
}
}
return self.attributesArray;
}
#pragma mark - 以下是拖动排序代码
- (void)setCanDrag:(BOOL)canDrag
{
_canDrag = canDrag;
if (canDrag) {
if (self.longPress == nil && self.panGesture == nil) {
[self setUpGestureRecognizers];
}
} else {
[self.collectionView removeGestureRecognizer:self.longPress];
self.longPress.delegate = nil;
self.longPress = nil;
[self.collectionView removeGestureRecognizer:self.panGesture];
self.panGesture.delegate = self;
self.panGesture = nil;
}
}
#pragma mark - observe
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"collectionView"]) {
if (self.canDrag) {
[self setUpGestureRecognizers];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setUpGestureRecognizers
{
if (self.collectionView == nil) {
return;
}
self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
self.longPress.delegate = self;
self.panGesture.delegate = self;
self.panGesture.maximumNumberOfTouches = 1;
NSArray *gestures = [self.collectionView gestureRecognizers];
__weak typeof(LBCollectionViewBaseLayout *) weakSelf = self;
[gestures enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[UILongPressGestureRecognizer class]]) {
[(UILongPressGestureRecognizer *)obj requireGestureRecognizerToFail:weakSelf.longPress];
}
}];
[self.collectionView addGestureRecognizer:self.longPress];
[self.collectionView addGestureRecognizer:self.panGesture];
}
#pragma mark - gesture
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPress
{
CGPoint location = [longPress locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
if (_cellFakeView != nil) {
indexPath = self.cellFakeView.indexPath;
}
if (indexPath != nil) {
return;
}
switch (longPress.state) {
case UIGestureRecognizerStateBegan:
{
self.collectionView.scrollsToTop = NO;
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
self.cellFakeView = [[LBCellFakeView alloc] initWithCell:cell];
self.cellFakeView.indexPath = indexPath;
self.cellFakeView.originCenter = cell.center;
self.cellFakeView.cellFrame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
[self.collectionView addSubview:self.cellFakeView];
self.fakeCellCenter = self.cellFakeView.center;
[self invalidateLayout];
[self.cellFakeView pushForwardView];
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
{
[self cancelDrag:indexPath];
}
default:
break;
}
}
//pan gesture
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
_panTranslation = [pan translationInView:self.collectionView];
if (_cellFakeView != nil) {
switch (pan.state) {
case UIGestureRecognizerStateChanged:
{
CGPoint center = _cellFakeView.center;
center.x = self.fakeCellCenter.x + self.panTranslation.x;
center.y = self.fakeCellCenter.y + self.panTranslation.y;
self.cellFakeView.center = center;
[self beginScrollIfNeeded];
[self moveItemIfNeeded];
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
{
[self invalidateDisplayLink];
}
break;
default:
break;
}
}
}
#pragma mark - gesturedelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint location = [gestureRecognizer locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
if (!indexPath) {
return NO;
}
if ([gestureRecognizer isEqual:self.longPress]) {
return (self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStatePossible ||
self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStateFailed);
} else if ([gestureRecognizer isEqual:self.panGesture]) {
return (self.longPress.state != UIGestureRecognizerStatePossible &&
self.longPress.state != UIGestureRecognizerStateFailed);
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([self.panGesture isEqual:gestureRecognizer]) {
return [self.longPress isEqual:otherGestureRecognizer];
} else if ([self.collectionView.panGestureRecognizer isEqual:gestureRecognizer]) {
return (self.longPress.state != UIGestureRecognizerStatePossible &&
self.longPress.state != UIGestureRecognizerStateFailed);
}
return YES;
}
- (void)cancelDrag:(NSIndexPath *)indexPath
{
if (self.cellFakeView == nil) {
return;
}
self.collectionView.scrollsToTop = YES;
self.fakeCellCenter = CGPointZero;
[self invalidateDisplayLink];
__weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
[self.cellFakeView pushBackView:^{
[weakSelf.cellFakeView removeFromSuperview];
weakSelf.cellFakeView = nil;
[weakSelf invalidateLayout];
}];
}
- (void)moveItemIfNeeded
{
NSIndexPath *atIndexPath = nil;
NSIndexPath *toIndexPath = nil;
__weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
if (self.cellFakeView) {
atIndexPath = self.cellFakeView.indexPath;
toIndexPath = [self.collectionView indexPathForItemAtPoint:self.cellFakeView.center];
}
if (atIndexPath.section != toIndexPath.section) {
return;
}
if (atIndexPath == nil || toIndexPath == nil) {
return;
}
if ([atIndexPath isEqual:toIndexPath]) {
return;
}
UICollectionViewLayoutAttributes *attribute = nil;
for (LBCollectionViewLayoutAttributes *attr in weakSelf.attributesArray) {
if (attr.indexPath.section == toIndexPath.section && attr.indexPath.item == toIndexPath.item
&& ![attr.representedElementKind isEqualToString: UICollectionElementKindSectionHeader]
&& ![attr.representedElementKind isEqualToString: UICollectionElementKindSectionFooter]) {
attribute = attr;
break;;
}
}
if (attribute != nil) {
[self.collectionView performBatchUpdates:^{
weakSelf.cellFakeView.indexPath = toIndexPath;
weakSelf.cellFakeView.cellFrame = attribute.frame;
[weakSelf.cellFakeView changeBoundsIfNeeded:attribute.bounds];
[weakSelf.collectionView moveItemAtIndexPath:atIndexPath toIndexPath:toIndexPath];
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) {
[weakSelf.delegate collectionview:weakSelf.collectionView layout:weakSelf didMoveCell:atIndexPath toIndexPath:toIndexPath];
}
} completion:nil];
}
}
- (void)setUpDisPlayLink
{
if (_displayLink) {
return;
}
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(continuousScroll)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)invalidateDisplayLink
{
_continousScrollDirection = LBScrollDirectionStay;
[_displayLink invalidate];
_displayLink = nil;
}
- (void)continuousScroll
{
if(_cellFakeView == nil) {
return;
}
CGFloat percentage = [self calcTriggerPercentage];
CGFloat scrollRate = [self scrollValueWithSpeed:10 andPercentage:percentage];
CGFloat offset = 0;
CGFloat insetTop = 0;
CGFloat insetEnd = 0;
CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat contentLength = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
self.collectionView.contentSize.height : self.collectionView.contentSize.width;
if (contentLength + insetTop + insetEnd <= length) {
return;
}
if (offset + scrollRate <= - insetTop) {
scrollRate = - insetEnd - offset;
} else if (offset + scrollRate >= contentLength + insetEnd - length) {
scrollRate = contentLength + insetEnd - length - offset;
}
__weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
[self.collectionView performBatchUpdates:^{
if (weakSelf.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGPoint point = weakSelf.fakeCellCenter;
point.y += scrollRate;
weakSelf.fakeCellCenter = point;
CGPoint center = weakSelf.cellFakeView.center;
center.y = weakSelf.fakeCellCenter.y + weakSelf.panTranslation.y;
weakSelf.cellFakeView.center = center;
CGPoint contentOffset = weakSelf.collectionView.contentOffset;
contentOffset.y += scrollRate;
weakSelf.collectionView.contentOffset = contentOffset;
} else {
CGPoint point = weakSelf.fakeCellCenter;
point.x += scrollRate;
weakSelf.fakeCellCenter = point;
CGPoint center = weakSelf.cellFakeView.center;
center.x = weakSelf.fakeCellCenter.x + weakSelf.panTranslation.x;
weakSelf.cellFakeView.center = center;
CGPoint contentOffset = weakSelf.collectionView.contentOffset;
contentOffset.x += scrollRate;
weakSelf.collectionView.contentOffset = contentOffset;
}
} completion:^(BOOL finished) {
}];
}
- (CGFloat)calcTriggerPercentage
{
if (_cellFakeView == nil) {
return 0;
}
CGFloat offset = 0;
CGFloat offsetEnd = 0 + self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat insetTop = 0;
CGFloat triggerInsetTop = 0;
CGFloat triggerInsetEnd = 0;
CGFloat paddingTop = 0;
CGFloat paddingEnd = 0;
CGFloat percentage = 0;
if (self.continousScrollDirection == LBScrollDirectionTop) {
if (self.cellFakeView) {
percentage = 1 - (((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) - (offset + paddingTop)) / triggerInsetTop);
}
} else if (self.continousScrollDirection == LBScrollDirectionBottom) {
if (self.cellFakeView) {
percentage = 1.0 - (((insetTop + offsetEnd - paddingEnd) - ((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) + (self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.size.height : self.cellFakeView.frame.size.width) + insetTop)) / triggerInsetEnd);
}
}
percentage = fmin(1.0f, percentage);
percentage = fmax(0, percentage);
return percentage;
}
- (void)beginScrollIfNeeded
{
if (self.cellFakeView == nil) {
return;
}
CGFloat offset = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentOffset.y : self.collectionView.contentOffset.x;
CGFloat triggerInsetTop = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
self.collectionView.contentInset.top : self.collectionView.contentInset.left;
CGFloat triggerInsetEnd = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
self.collectionView.contentInset.bottom : self.collectionView.contentInset.right;
CGFloat paddingTop = 0;
CGFloat paddingend = 0;
CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat fakeCellTopEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMinY(self.cellFakeView.frame) : CGRectGetMinX(self.cellFakeView.frame);
CGFloat fakeCellEndEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMaxY(self.cellFakeView.frame) : CGRectGetMaxX(self.cellFakeView.frame);
if (fakeCellTopEdge <= offset + paddingTop + triggerInsetTop) {
self.continousScrollDirection = LBScrollDirectionTop;
[self setUpDisPlayLink];
} else if (fakeCellEndEdge >= offset + length - paddingend - triggerInsetEnd) {
self.continousScrollDirection = LBScrollDirectionBottom;
[self setUpDisPlayLink];
} else {
[self invalidateDisplayLink];
}
}
#pragma mark - getter
- (CGFloat)scrollValueWithSpeed:(CGFloat)speed
andPercentage:(CGFloat)percentage
{
CGFloat value = 0.0f;
switch (self.continousScrollDirection) {
case LBScrollDirectionStay:
return 0;
break;
case LBScrollDirectionTop:
value = -speed;
break;
case LBScrollDirectionBottom:
value = speed;
break;
default:
return 0;
break;
}
CGFloat proofedPercentage = fmax(fmin(1, percentage), 0);
return value * proofedPercentage;
}
- (void)forceSetIsNeedReCaculateAllLayout:(BOOL)isNeedReCaculateAllLayout
{
_isNeedReCalculateAllLayout = isNeedReCaculateAllLayout;
}
@end
子类layout 中的代码
//
// LBHorizontalLayout.m
// TEXT
//
// Created by mac on 2024/5/18.
// Copyright © 2024 刘博. All rights reserved.
//
#import "LBHorizontalLayout.h"
#import "LBCollectionReusableView.h"
#import "LBCollectionViewLayoutAttributes.h"
#import "LBCollectionViewBackgroundViewLayoutAttributes.h"
@implementation LBHorizontalLayout
#pragma mark - 初始化属性
- (instancetype)init
{
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
- (void)prepareLayout
{
[super prepareLayout];
if (!self.isNeedReCalculateAllLayout) {
//不需要重新计算
return;
}
CGFloat totalHeight = self.collectionView.frame.size.height;
CGFloat x = 0;
CGFloat y = 0;
CGFloat headerW = 0;
CGFloat footerW = 0;
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
CGFloat minimumLineSpacing = 0;
CGFloat minimumInterItemSpacing = 0;
NSInteger sectionCount = [self.collectionView numberOfSections];
self.attributesArray = [NSMutableArray array];
self.collectionHeightsArray = [NSMutableArray array];
for (int index = 0; index < sectionCount; index ++) {
NSInteger itemCount = [self.collectionView numberOfItemsInSection:index];
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
headerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:index].width;
} else {
headerW = self.headerReferenceSize.width;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:index].width;
} else {
footerW = self.footerReferenceSize.width;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:index];
} else {
edgeInsets = self.sectionInset;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]) {
minimumLineSpacing = [self.delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:index];
} else {
minimumLineSpacing = self.minimumLineSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
minimumInterItemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:index];
} else {
minimumInterItemSpacing = self.minimumInteritemSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString *className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
NSAssert([[NSClassFromString(className) alloc] init] != nil, @"代理collectionView:layout:registerBackView:里面必须返回有效的类名");
[self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
} else {
[self registerClass:[LBCollectionReusableView class] forDecorationViewOfKind:@"LBCollectionReusableView"];
}
} else {
[self registerClass:[LBCollectionReusableView class] forDecorationViewOfKind:@"LBCollectionReusableView"];
}
x = [self maxHeightWithSection:index];
y = edgeInsets.top;
if (headerW > 0) {
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
LBCollectionViewLayoutAttributes *headerAttr = [LBCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:headerIndexPath];
/*注意,因为这里是水平布局,所以self.collectionView.frame.size.height 是一个固定的很小的
值
*/
headerAttr.frame = CGRectMake(x, 0, headerW, self.collectionView.frame.size.height);
[headerAttr setValue:[NSValue valueWithCGRect:headerAttr.frame] forKey:@"orginalFrame"];
[self.attributesArray addObject:headerAttr];
[self.headerAttributesArray addObject:headerAttr];
}
x += headerW;
CGFloat itemStartX = x;
CGFloat lastX = x;
if (itemCount > 0) {
x += edgeInsets.left;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:typeOfLayout:)]) {
self.layoutType = [self.delegate collectionView:self.collectionView layout:self typeOfLayout:index];
}
NSAssert((self.layoutType == LBLayoutTypeLabelVerticalLayout ||
self.layoutType == LBLayoutTypeColumnLayout ||
self.layoutType == LBLayoutTypeAbsoluteLayout), @"横向布局暂时只支持 LBLayoutTypeLabelVerticalLayout, LBLayoutTypeColumnLayout, LBLayoutTypeAbsoluteLayout");
//NSInteger columnCount = 1;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountOfSection:)]) {
self.columnCount = [self.delegate collectionView:self.collectionView layout:self columnCountOfSection:index];
}
//定义一个列高数组,记录每一列的总高度
CGFloat *columnWidths = (CGFloat *)malloc(self.columnCount * sizeof(CGFloat));
//cell的高度
CGFloat itemHeight = 0.0;
if (self.layoutType == LBLayoutTypeColumnLayout) {
for (int i = 0; i < self.columnCount; i ++) {
if (i == 0 && self.topLeftGap > 0) {
columnWidths[i] = self.topLeftGap + x;
} else if (i == 1 && self.bottomLeftGap > 0) {
columnWidths[i] = self.bottomLeftGap;
} else {
columnWidths[i] = x;
}
}
itemHeight = (totalHeight - edgeInsets.top - edgeInsets.bottom - minimumLineSpacing * (self.columnCount - 1)) / self.columnCount;
}
NSInteger lastColumnIndex = 0;
NSMutableArray *arrayOfAbsolute = [NSMutableArray array]; //储存绝对定位布局的额数组
for (int i = 0; i < itemCount; i ++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:index];
CGSize itemSize = CGSizeZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {
itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
} else {
itemSize = self.itemSize;
}
LBCollectionViewLayoutAttributes *attributes = [LBCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSInteger preRow = self.attributesArray.count - 1;
switch (self.layoutType) {
#pragma mark - 纵向标签布局处理
case LBLayoutTypeLabelVerticalLayout:
{
if (preRow >= 0) {
if (i > 0) {
LBCollectionViewLayoutAttributes *preAttr = self.attributesArray[preRow];
y = preAttr.frame.origin.y + preAttr.frame.size.height + minimumInterItemSpacing;
if (y + itemSize.height > totalHeight - edgeInsets.bottom) {
y = edgeInsets.top;
x += itemSize.width + minimumLineSpacing;
}
}
}
attributes.frame = CGRectMake(x, y, itemSize.width, itemSize.height);
}
break;
case LBLayoutTypeLabelHorizontalLayout: {
}
break;
#pragma mark - 列布局处理 |横向标签布局处理
case LBLayoutTypeColumnLayout:
{
CGFloat max = CGFLOAT_MAX;
NSInteger column = 0;
if (self.columnSortType == LBColumnSortTypeSequence) {
column = lastColumnIndex;
} else {
for (int i = 0; i < self.columnCount; i ++) {
if (columnWidths[i] < max) {
max = columnWidths[i];
column = i;
}
}
}
CGFloat itemX = columnWidths[column];
CGFloat itemY = edgeInsets.top + (itemHeight + minimumInterItemSpacing) * column;
if (self.manulHeight) {
attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemSize.height);
} else {
attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemHeight);
}
NSLog(@"哈哈哈这里的布局%@", NSStringFromCGRect(attributes.frame));
if (self.manulHeight) {
if (itemSize.height > itemHeight) {
CGFloat delta = itemSize.height - itemHeight;
if (delta > minimumInterItemSpacing) {
NSInteger k = ceil(delta/(itemHeight + minimumInterItemSpacing));
for (int i = 0; i < k ; i ++) {
if ((column + i + 1) < self.columnCount) {
columnWidths[column + i + 1] += (itemSize.width + minimumLineSpacing);
}
}
}
}
}
columnWidths[column] += (itemSize.width + minimumLineSpacing);
lastColumnIndex ++;
if (lastColumnIndex >= self.columnCount) {
lastColumnIndex = 0;
}
}
break;
case LBLayoutTypeAbsoluteLayout:
{
CGRect itemFrame = CGRectZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:rectOfItem:)]) {
itemFrame = [self.delegate collectionView:self.collectionView layout:self rectOfItem:indexPath];
}
CGFloat absolute_x = x + itemFrame.origin.x;
CGFloat absolute_y = edgeInsets.top + itemFrame.origin.y;
CGFloat absolute_h = itemFrame.size.height;
if ((absolute_y + absolute_h > self.collectionView.frame.size.height - edgeInsets.bottom) &&
(absolute_y < self.collectionView.frame.size
.height - edgeInsets.top)) {
absolute_h -= (absolute_y + absolute_h - (self.collectionView.frame.size.height - edgeInsets.bottom));
}
CGFloat absolute_w = itemFrame.size.width;
attributes.frame = CGRectMake(absolute_x, absolute_y, absolute_w, absolute_h);
[arrayOfAbsolute addObject:attributes];
}
break;
default:
break;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:transformOfItem:)]) {
attributes.transform3D = [self.delegate collectionView:self.collectionView layout:self transformOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:zIndexOfItem:)]) {
attributes.zIndex = [self.delegate collectionView:self.collectionView layout:self zIndexOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:alphaOfItem:)]) {
attributes.alpha = [self.delegate collectionView:self.collectionView layout:self alphaOfItem:indexPath];
}
attributes.indexPath = indexPath;
if (self.layoutType != LBLayoutTypePercentLayout) {
[self.attributesArray addObject:attributes];
}
if(self.layoutType == LBLayoutTypeColumnLayout) {
CGFloat max = 0;
for (int i = 0; i < self.columnCount; i ++) {
if (columnWidths[i] > max) {
max = columnWidths[i];
}
}
lastX = max;
} else if (self.layoutType == LBLayoutTypeAbsoluteLayout) {
if (i == itemCount - 1) {
for (LBCollectionViewLayoutAttributes *attr in arrayOfAbsolute) {
if (lastX < attr.frame.origin.x + attr.frame.size.width) {
lastX = attr.frame.origin.x + attr.frame.size.width;
}
}
}
} else {
lastX = attributes.frame.origin.x + attributes.frame.size.width;
}
}
free(columnWidths);
}
if (self.layoutType == LBLayoutTypeColumnLayout) {
if (itemCount > 0) {
lastX -= minimumLineSpacing;
}
}
if (itemCount > 0) {
lastX += edgeInsets.right;
}
//添加页脚属性
if (footerW > 0) {
NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
LBCollectionViewLayoutAttributes *footerAttr = [LBCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:footerIndexPath];
footerAttr.frame = CGRectMake(lastX, 0, footerW, self.collectionView.frame.size.height);
[self.attributesArray addObject:footerAttr];
lastX += footerW;
}
//添加背景视图
CGFloat backWidth = lastX - itemStartX + ([self isAttachToBottom:index] ? headerW : 0) - ([self isAttachToTop:index] ? 0: footerW);
if (backWidth < 0) {
backWidth = 0;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString *className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
LBCollectionViewBackgroundViewLayoutAttributes *attr = [LBCollectionViewBackgroundViewLayoutAttributes layoutAttributesForDecorationViewOfKind:className withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW: itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.zIndex = - 1000;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backgroundViewMethodForSection:)]) {
if ([self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index] != nil) {
[attr callMethod:[self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index]];
}
}
[self.attributesArray addObject:attr];
} else {
LBCollectionViewLayoutAttributes *attr = [LBCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"LBCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW : itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = - 1000;
[self.attributesArray addObject:attr];
}
} else {
LBCollectionViewLayoutAttributes *attr = [LBCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"LBCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW : itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = - 1000;
[self.attributesArray addObject:attr];
}
self.collectionHeightsArray[index] = [NSNumber numberWithFloat:lastX];
}
[self forceSetIsNeedReCaculateAllLayout:NO];
}
#pragma mark - 内容size
- (CGSize)collectionViewContentSize
{
if (self.collectionHeightsArray.count < 0) {
return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height);
}
CGFloat footerW = 0;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:self.collectionHeightsArray.count - 1].width;
} else {
footerW = self.footerReferenceSize.width;
}
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:self.collectionHeightsArray.count - 1];
} else {
edgeInsets = self.sectionInset;
}
return CGSizeMake([self.collectionHeightsArray[self.collectionHeightsArray.count - 1] floatValue], self.collectionView.frame.size.height);
}
- (BOOL)isAttachToTop:(NSInteger)section
{
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToTop:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToTop:section];
}
return NO;
}
- (BOOL)isAttachToBottom:(NSInteger)section
{
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToBottom:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToBottom:section];
}
return NO;
}
/// 每个区的初始x坐标
/// - Parameter section: 区索引
- (CGFloat)maxHeightWithSection:(NSInteger)section
{
if (section > 0) {
return [self.collectionHeightsArray[section - 1] floatValue];
} else {
return 0;
}
}
@end