赞
踩
首先查看效果图
是支持多分区的
思路就是和竖直方向上的瀑布流布局是一样的,
只不过这里记录的列是水平方向的,同时要记录下
当前最小的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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。