元素流排版
//
// LMJElementsFlowLayout.m
//
// Created by apple on 17/4/19.
// Copyright © 2017年 NJHu. All rights reserved.
//
#import "LMJElementsFlowLayout.h"
#define LMJXX(x) floorf(x)
#define LMJXS(s) ceilf(s)
static const CGFloat LMJ_XMargin_ = 10;
static const CGFloat LMJ_YMargin_ = 10;
static const UIEdgeInsets LMJ_EdgeInsets_ = {20, 10, 10, 10};
@interface LMJElementsFlowLayout()
/** 所有的cell的attrbts */
@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *lmj_AtrbsArray;
/** 每一列的最后的高度 */
/** <#digest#> */
@property (assign, nonatomic) CGRect lmj_LastAtrbsFrame;
- (CGFloat)xMarginAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)yMarginAtIndexPath:(NSIndexPath *)indexPath;
- (UIEdgeInsets)edgeInsets;
- (CGRect)maxHeightFrame;
@end
@implementation LMJElementsFlowLayout
/**
* 刷新布局的时候回重新调用
*/
- (void)prepareLayout
{
[super prepareLayout];
//如果重新刷新就需要移除之前存储的高度
//复赋值以顶部的高度, 并且根据列数
self.lmj_LastAtrbsFrame = CGRectMake(0, 0, self.collectionView.frame.size.width, 0);
// 移除以前计算的cells的attrbs
[self.lmj_AtrbsArray removeAllObjects];
// 并且重新计算, 每个cell对应的atrbs, 保存到数组
for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
{
[self.lmj_AtrbsArray addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
}
}
/**
*在这里边所处每个cell对应的位置和大小
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *atrbs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
// 取到设置的size
CGSize itemSize = [self.delegate waterflowLayout:self collectionView:self.collectionView sizeForItemAtIndexPath:indexPath];
//向下取整宽度
CGFloat w = LMJXX(itemSize.width);
//取可显示区域的最小值,不超过边界
w = MIN(w, self.collectionView.frame.size.width);
// 高度由外界决定, 外界必须实现这个方法
CGFloat h = itemSize.height;
// 拿到最后的高度最小的那一列, 假设第0列最小
//剩余可用布局宽度
//collectionView宽度-上一个布局的最大origin.x-列间距-Insets距右
CGFloat rightLeftWidth = self.collectionView.frame.size.width - CGRectGetMaxX(self.lmj_LastAtrbsFrame) - [self xMarginAtIndexPath:indexPath] - self.edgeInsets.right;
CGFloat x = self.edgeInsets.left;
CGFloat y = self.edgeInsets.top;
//如果横向剩余宽度还有空间排版
if (rightLeftWidth >= w) {
//上一个视图的最大origin.x+列间距
x = CGRectGetMaxX(self.lmj_LastAtrbsFrame) + [self xMarginAtIndexPath:indexPath];
y = self.lmj_LastAtrbsFrame.origin.y;
}else//换行进行排版
{
x = self.edgeInsets.left;
//取得最大高度
// 上一个视图的最大origin.y+行间距
y = CGRectGetMaxY(self.maxHeightFrame) + [self yMarginAtIndexPath:indexPath];
}
//如果内容区域超过collectionView可展示区域时候,中间排布方式,不要设置itemSize.width过大情况,导致edgeInsets失效
if (w > self.collectionView.frame.size.width - self.edgeInsets.left - self.edgeInsets.right) {
x = (self.collectionView.frame.size.width - w) * 0.5;
}
//如果小于行间距,就取行间距
if (y <= [self yMarginAtIndexPath:indexPath]) {
y = self.edgeInsets.top;
}
// 赋值frame
atrbs.frame = CGRectMake(x, y, w, h);
// 覆盖添加完后那一列的最新高度
self.lmj_LastAtrbsFrame = atrbs.frame;
return atrbs;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.lmj_AtrbsArray;
}
- (CGRect)maxHeightFrame
{
__block CGRect maxHeightFrame = self.lmj_LastAtrbsFrame;
[self.lmj_AtrbsArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (CGRectGetMaxY(obj.frame) > CGRectGetMaxY(maxHeightFrame)) {
maxHeightFrame = obj.frame;
}
}];
return maxHeightFrame;
}
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.collectionView.frame.size.width, CGRectGetMaxY(self.maxHeightFrame) + self.edgeInsets.bottom);
}
- (NSMutableArray *)lmj_AtrbsArray
{
if(_lmj_AtrbsArray == nil)
{
_lmj_AtrbsArray = [NSMutableArray array];
}
return _lmj_AtrbsArray;
}
- (CGFloat)xMarginAtIndexPath:(NSIndexPath *)indexPath
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:collectionView:columnsMarginForItemAtIndexPath:)])
{
return [self.delegate waterflowLayout:self collectionView:self.collectionView columnsMarginForItemAtIndexPath:indexPath];
}
else
{
return LMJ_XMargin_;
}
}
- (CGFloat)yMarginAtIndexPath:(NSIndexPath *)indexPath
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:collectionView:linesMarginForItemAtIndexPath:)])
{
return [self.delegate waterflowLayout:self collectionView:self.collectionView linesMarginForItemAtIndexPath:indexPath];
}else
{
return LMJ_YMargin_;
}
}
- (UIEdgeInsets)edgeInsets
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:edgeInsetsInCollectionView:)])
{
return [self.delegate waterflowLayout:self edgeInsetsInCollectionView:self.collectionView];
}
else
{
return LMJ_EdgeInsets_;
}
}
- (id<LMJElementsFlowLayoutDelegate>)delegate
{
return (id<LMJElementsFlowLayoutDelegate>)self.collectionView.dataSource;
}
- (instancetype)initWithDelegate:(id<LMJElementsFlowLayoutDelegate>)delegate
{
if (self = [super init]) {
}
return self;
}
+ (instancetype)flowLayoutWithDelegate:(id<LMJElementsFlowLayoutDelegate>)delegate
{
return [[self alloc] initWithDelegate:delegate];
}
@end
水平方向流水布局
//
// LMJHorizontalFlowLayout.m
// 瀑布流完善接口
//
// Created by apple on 16/7/31.
// Copyright © 2016年 NJHu. All rights reserved.
//
#import "LMJHorizontalFlowLayout.h"
#define LMJXX(x) floorf(x)
#define LMJXS(s) ceilf(s)
static const NSInteger LMJ_Lines_ = 3;
static const CGFloat LMJ_XMargin_ = 10;
static const CGFloat LMJ_YMargin_ = 10;
static const UIEdgeInsets LMJ_EdgeInsets_ = {10, 10, 10, 10};
@interface LMJHorizontalFlowLayout()
/** 所有的cell的attrbts */
@property (nonatomic, strong) NSMutableArray *lmj_AtrbsArray;
/** 每一列的最后的宽度GetMaxX*/
@property (nonatomic, strong) NSMutableArray *lmj_LinesWidthArray;
/// 获取多少行
- (NSInteger)lines;
///列间距
- (CGFloat)xMarginAtIndexPath:(NSIndexPath *)indexPath;
///行间距
- (CGFloat)yMargin;
///边框edgeInsets
- (UIEdgeInsets)edgeInsets;
@end
@implementation LMJHorizontalFlowLayout
/**
* 刷新布局的时候回重新调用
*/
- (void)prepareLayout
{
[super prepareLayout];
//如果重新刷新就需要移除之前存储的高度
[self.lmj_LinesWidthArray removeAllObjects];
//复赋值以顶部的高度, 并且根据列数
for (NSInteger i = 0; i < self.lines; i++) {
[self.lmj_LinesWidthArray addObject:@(self.edgeInsets.left)];
}
// 移除以前计算的cells的attrbs
[self.lmj_AtrbsArray removeAllObjects];
// 并且重新计算, 每个cell对应的atrbs, 保存到数组
for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
{
[self.lmj_AtrbsArray addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
}
}
/**
*在这里边所处每个cell对应的位置和大小
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *atrbs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//根据列数和间距计算item实际占用的高度,固定不变
CGFloat h = 1.0 * (self.collectionView.frame.size.height - self.edgeInsets.top - self.edgeInsets.bottom - self.yMargin * (self.lines - 1)) / self.lines;
//向下取整
h = LMJXX(h);
// 宽度由外界决定, 外界必须实现这个方法
CGFloat w = [self.delegate waterflowLayout:self collectionView:self.collectionView widthForItemAtIndexPath:indexPath itemHeight:h];
// 拿到GetMaxX最小Item的那个开始拍版
NSInteger indexLine = 0;
CGFloat minLineW = [self.lmj_LinesWidthArray[indexLine] doubleValue];
for (NSInteger i = 1; i < self.lmj_LinesWidthArray.count; i++)
{
CGFloat lineW = [self.lmj_LinesWidthArray[i] doubleValue];
if(minLineW > lineW)
{
minLineW = lineW;
indexLine = i;
}
}
//列间距+保存的最小item的GetMaxX开始排版
CGFloat x = [self xMarginAtIndexPath:indexPath] + minLineW;
//如果第一次初始排版,设置等于edgeInsets.left
if (minLineW == self.edgeInsets.left) {
x = self.edgeInsets.left;
}
//edgeInsets.top+(行间距+高度)*行数
CGFloat y = self.edgeInsets.top + (self.yMargin + h) * indexLine;
// 赋值frame
atrbs.frame = CGRectMake(x, y, w, h);
// 覆盖添加完后那一列的最新item的GetMaxX位置
self.lmj_LinesWidthArray[indexLine] = @(CGRectGetMaxX(atrbs.frame));
return atrbs;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.lmj_AtrbsArray;
}
- (CGSize)collectionViewContentSize
{
__block CGFloat maxColW = [self.lmj_LinesWidthArray.firstObject doubleValue];
[self.lmj_LinesWidthArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (maxColW < [obj doubleValue]) {
maxColW = [obj doubleValue];
}
}];
//最后一次排版的位置加上edgeInsets.right作为ContentSize的宽度
return CGSizeMake(maxColW + self.edgeInsets.right, self.collectionView.frame.size.height);
}
- (NSMutableArray *)lmj_AtrbsArray
{
if(_lmj_AtrbsArray == nil)
{
_lmj_AtrbsArray = [NSMutableArray array];
}
return _lmj_AtrbsArray;
}
- (NSMutableArray *)lmj_LinesWidthArray
{
if(_lmj_LinesWidthArray == nil)
{
_lmj_LinesWidthArray = [NSMutableArray array];
}
return _lmj_LinesWidthArray;
}
- (NSInteger)lines
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:linesInCollectionView:)])
{
return [self.delegate waterflowLayout:self linesInCollectionView:self.collectionView];
}
else
{
return LMJ_Lines_;
}
}
- (CGFloat)xMarginAtIndexPath:(NSIndexPath *)indexPath
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:collectionView:columnsMarginForItemAtIndexPath:)])
{
return [self.delegate waterflowLayout:self collectionView:self.collectionView columnsMarginForItemAtIndexPath:indexPath];
}
else
{
return LMJ_XMargin_;
}
}
- (CGFloat)yMargin
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:linesMarginInCollectionView:)])
{
return [self.delegate waterflowLayout:self linesMarginInCollectionView:self.collectionView];
}else
{
return LMJ_YMargin_;
}
}
- (UIEdgeInsets)edgeInsets
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:edgeInsetsInCollectionView:)])
{
return [self.delegate waterflowLayout:self edgeInsetsInCollectionView:self.collectionView];
}
else
{
return LMJ_EdgeInsets_;
}
}
- (id<LMJHorizontalFlowLayoutDelegate>)delegate
{
return (id<LMJHorizontalFlowLayoutDelegate>)self.collectionView.dataSource;
}
- (instancetype)initWithDelegate:(id<LMJHorizontalFlowLayoutDelegate>)delegate
{
if (self = [super init]) {
}
return self;
}
+ (instancetype)flowLayoutWithDelegate:(id<LMJHorizontalFlowLayoutDelegate>)delegate
{
return [[self alloc] initWithDelegate:delegate];
}
@end
垂直方向流水布局
//
// LMJVerticalFlowLayout.m
// 瀑布流完善接口
//
// Created by apple on 16/7/31.
// Copyright © 2016年 NJHu. All rights reserved.
//
#import "LMJVerticalFlowLayout.h"
#define LMJXX(x) floorf(x)
#define LMJXS(s) ceilf(s)
static const NSInteger LMJ_Columns_ = 3;
static const CGFloat LMJ_XMargin_ = 10;
static const CGFloat LMJ_YMargin_ = 10;
static const UIEdgeInsets LMJ_EdgeInsets_ = {20, 10, 10, 10};
@interface LMJVerticalFlowLayout()
/** 所有的cell的attrbts */
@property (nonatomic, strong) NSMutableArray *lmj_AtrbsArray;
/** 每一列的最后的高度 */
@property (nonatomic, strong) NSMutableArray *lmj_ColumnsHeightArray;
- (NSInteger)columns;
- (CGFloat)xMargin;
- (CGFloat)yMarginAtIndexPath:(NSIndexPath *)indexPath;
- (UIEdgeInsets)edgeInsets;
@end
@implementation LMJVerticalFlowLayout
/**
* 刷新布局的时候回重新调用
*/
- (void)prepareLayout
{
[super prepareLayout];
//如果重新刷新就需要移除之前存储的高度
[self.lmj_ColumnsHeightArray removeAllObjects];
//复赋值以顶部的高度, 并且根据列数
for (NSInteger i = 0; i < self.columns; i++) {
[self.lmj_ColumnsHeightArray addObject:@(self.edgeInsets.top)];
}
// 移除以前计算的cells的attrbs
[self.lmj_AtrbsArray removeAllObjects];
// 并且重新计算, 每个cell对应的atrbs, 保存到数组
for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
{
[self.lmj_AtrbsArray addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
}
}
/**
*在这里边所处每个cell对应的位置和大小
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *atrbs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//计算宽度 保持不变
CGFloat w = 1.0 * (self.collectionView.frame.size.width - self.edgeInsets.left - self.edgeInsets.right - self.xMargin * (self.columns - 1)) / self.columns;
//向下取整
w = LMJXX(w);
// 高度由外界决定, 外界必须实现这个方法
CGFloat h = [self.delegate waterflowLayout:self collectionView:self.collectionView heightForItemAtIndexPath:indexPath itemWidth:w];
// 拿到最后的高度最小Item的getmaxY的那一列, 假设第0列最小
NSInteger indexCol = 0;
CGFloat minColH = [self.lmj_ColumnsHeightArray[indexCol] doubleValue];
for (NSInteger i = 1; i < self.lmj_ColumnsHeightArray.count; i++)
{
CGFloat colH = [self.lmj_ColumnsHeightArray[i] doubleValue];
if(minColH > colH)
{
minColH = colH;
indexCol = i;
}
}
//edgeInsets.left+(列间距+宽度)*最小getmaxY的所在列
CGFloat x = self.edgeInsets.left + (self.xMargin + w) * indexCol;
//最小getmaxY的所在列的maxY+行间距
CGFloat y = minColH + [self yMarginAtIndexPath:indexPath];
// 是第一次排版 高度edgeInsets.top
if (minColH == self.edgeInsets.top) {
y = self.edgeInsets.top;
}
// 赋值frame
atrbs.frame = CGRectMake(x, y, w, h);
// 覆盖添加完后那一列的最新最小item的GetMaxY位置
self.lmj_ColumnsHeightArray[indexCol] = @(CGRectGetMaxY(atrbs.frame));
return atrbs;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.lmj_AtrbsArray;
}
- (CGSize)collectionViewContentSize
{
CGFloat maxColH = [self.lmj_ColumnsHeightArray.firstObject doubleValue];
for (NSInteger i = 1; i < self.lmj_ColumnsHeightArray.count; i++)
{
CGFloat colH = [self.lmj_ColumnsHeightArray[i] doubleValue];
if(maxColH < colH)
{
maxColH = colH;
}
}
return CGSizeMake(self.collectionView.frame.size.width, maxColH + self.edgeInsets.bottom);
}
- (NSMutableArray *)lmj_AtrbsArray
{
if(_lmj_AtrbsArray == nil)
{
_lmj_AtrbsArray = [NSMutableArray array];
}
return _lmj_AtrbsArray;
}
- (NSMutableArray *)lmj_ColumnsHeightArray
{
if(_lmj_ColumnsHeightArray == nil)
{
_lmj_ColumnsHeightArray = [NSMutableArray array];
}
return _lmj_ColumnsHeightArray;
}
- (NSInteger)columns
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:columnsInCollectionView:)])
{
return [self.delegate waterflowLayout:self columnsInCollectionView:self.collectionView];
}
else
{
return LMJ_Columns_;
}
}
- (CGFloat)xMargin
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:columnsMarginInCollectionView:)])
{
return [self.delegate waterflowLayout:self columnsMarginInCollectionView:self.collectionView];
}
else
{
return LMJ_XMargin_;
}
}
- (CGFloat)yMarginAtIndexPath:(NSIndexPath *)indexPath
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:collectionView:linesMarginForItemAtIndexPath:)])
{
return [self.delegate waterflowLayout:self collectionView:self.collectionView linesMarginForItemAtIndexPath:indexPath];
}else
{
return LMJ_YMargin_;
}
}
- (UIEdgeInsets)edgeInsets
{
if([self.delegate respondsToSelector:@selector(waterflowLayout:edgeInsetsInCollectionView:)])
{
return [self.delegate waterflowLayout:self edgeInsetsInCollectionView:self.collectionView];
}
else
{
return LMJ_EdgeInsets_;
}
}
- (id<LMJVerticalFlowLayoutDelegate>)delegate
{
return (id<LMJVerticalFlowLayoutDelegate>)self.collectionView.dataSource;
}
- (instancetype)initWithDelegate:(id<LMJVerticalFlowLayoutDelegate>)delegate
{
if (self = [super init]) {
}
return self;
}
+ (instancetype)flowLayoutWithDelegate:(id<LMJVerticalFlowLayoutDelegate>)delegate
{
return [[self alloc] initWithDelegate:delegate];
}
@end