封装自定义瀑布流,实现原理

继承UICollectionViewLayout
@interface WaterPullLayout : UICollectionViewLayout

需要两个数组来保存每个item的属性和每列的总高度

@interface WaterPullLayout ()
{
    /**
     *  保存每个item的属性
     */
    NSMutableArray *_itemAttributes;

    /**
     *  保存每列的高度
     */
    NSMutableArray *_columnHeights;
}

.m实现必须重写四个方法:

-//对item布局,reloadData会触发
- (void)prepareLayout
- //返回指定区域的item的属性对象数组
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
-//返回指定indexPath的item的属性对象
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
-//collectionView的contentSize
- (CGSize)collectionViewContentSize

-#import 《UIKit/UIKit.h>
@protocol WaterPullLayoutDelegate

@required
- (CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout )collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;

@end

@interface WaterPullLayout : UICollectionViewLayout

/**
* 列数
*/
@property (nonatomic) NSInteger columnCount;

/**
* 距离上下左右的距离
*/
@property (nonatomic) UIEdgeInsets sectionInsets;

/**
* 两个cell的水平方向的空隙
*/
@property (nonatomic) CGFloat horizontalSpace;

/**
* 两个cell的垂直方向的空隙
*/
@property (nonatomic) CGFloat verticalSpace;

@end


#import "WaterPullLayout.h"

@interface WaterPullLayout ()
{
    /**
     *  保存每个cell的属性
     */
    NSMutableArray *_itemAttributes;

    /**
     *  保存每列的高度
     */
    NSMutableArray *_columnHeights;
}

@property (nonatomic, weak) id<WaterPullLayoutDelegate> delegate;

@end

@implementation WaterPullLayout

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        //默认2列
        _columnCount = 2;
        _horizontalSpace = 10;
        _verticalSpace = 10;
        _sectionInsets = UIEdgeInsetsZero;
    }
    return self;
}

/**
 *  返回高度最大的列的索引
 */
- (NSInteger)maxHeightColumnIndex
{
    __block NSInteger index = 0;
    __block CGFloat maxValue = 0;

    [_columnHeights enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
     {
         //如果从数组里面取出的元素比现有最大值还大,这个元素就是最大值
         if ([obj floatValue] > maxValue)
         {
             //替换最大值
             maxValue = [obj floatValue];

             //保存最大值的索引
             index = idx;
         }
     }];

    return index;
}

/**
 *  @return 返回高度最小的列的索引
 */
- (NSInteger)minHeightColumnIndex
{
    __block NSInteger index = 0;
    __block CGFloat minValue = MAXFLOAT;

    [_columnHeights enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
    {
        //如果从数组里面取出的元素比现有最小值还小,这个元素就是最小值
        if ([obj floatValue] < minValue)
        {
            //替换最小值
            minValue = [obj floatValue];

            //保存最小值的索引
            index = idx;
        }
    }];

    return index;
}

//对item布局,reloadData会触发
- (void)prepareLayout
{
    [super prepareLayout];

    self.delegate = (id<WaterPullLayoutDelegate>)self.collectionView.delegate;

    //初始化数组
    _itemAttributes = [NSMutableArray array];
    _columnHeights = [NSMutableArray array];

    //初始化默认高度
    for (int i = 0; i < self.columnCount; i++)
    {
        //每列的高度初始化都是0
        _columnHeights[i] = @(0);
    }

    //item的宽度 =(collectionView的宽 - 左边距 - 右边距 - item间的水平间隙 )/ 列数
    CGFloat itemWidth = (self.collectionView.frame.size.width - self.sectionInsets.left - self.sectionInsets.right - (self.columnCount - 1)*self.horizontalSpace)/self.columnCount;


    //计算每个item的位置坐标(只考虑一个组的情况)
    //[self.collectionView numberOfItemsInSection:0]返回指定组的item个数
    for (int i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
    {
        //获取item大小(调用collectionView的代理方法)
        CGSize size = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];

        //item的高度
        CGFloat itemHeight;
        //item的x坐标
        CGFloat xOffset;
        //item的y坐标
        CGFloat yOffset;

        //现在图片的高度
        /*
         原始H    X
         ---- =  ----- ===>  X = 原始H*现在W/原始W
         原始W    现在W
         */
        itemHeight = size.height * itemWidth / size.width;

        //获取列的高度的最小值索引
        NSInteger minIndex = [self minHeightColumnIndex];

        NSLog(@"%ld",minIndex);

        //item的x坐标 = 与左边间距 + 最小高度索引值*(item宽 + item间水平间隙)
        xOffset = self.sectionInsets.left + minIndex*(itemWidth+self.horizontalSpace);

        //item的y坐标 = 最小高度 + (item个数如果小于列数)?与顶部间距 :item间的垂直间隙
        yOffset = [_columnHeights[minIndex] floatValue] + ((i < self.columnCount) ? self.sectionInsets.top : self.verticalSpace);

        //item的frame
        CGRect itemFrame = CGRectMake(xOffset, yOffset, itemWidth, itemHeight);

        //属性
        UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        attribute.frame = itemFrame;

        //添加到数组中
        [_itemAttributes addObject:attribute];


        //修改当前列的高度
        _columnHeights[minIndex] = @(CGRectGetMaxY(itemFrame));
    }
}

/**
 *  返回指定区域的cell的属性对象数组
 *
 *  @param rect <#rect description#>
 *
 *  @return <#return value description#>
 */
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return _itemAttributes;
}

/**
 *  返回指定indexPath的item的属性对象
 *
 *  @param indexPath <#indexPath description#>
 */
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return _itemAttributes[indexPath.item];
}

/**
 *  collectionView的contentSize
 */
- (CGSize)collectionViewContentSize
{
    //取最大值的索引
    NSInteger maxIndex = [self maxHeightColumnIndex];

    //返回collectionView的contentSize
    return CGSizeMake(self.collectionView.frame.size.width, [_columnHeights[maxIndex] floatValue] + self.sectionInsets.bottom);
}

@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值