自定义的UICollectionViewFlowLayout

//=========================================.h=================================

//  TopBarLayout.h

//  LOLADFNeverGiveUp

//

//  Created by 曹老师 on 2018/12/17.

//  Copyright © 2018  All rights reserved.

//

#import

NS_ASSUME_NONNULL_BEGIN

@interface ZoneImageFlowLayout : UICollectionViewFlowLayout

// item 的行距(默认4.0)

@property (nonatomic, assign) CGFloat lineSpacing;

// item 的间距 (默认4.0)

@property (nonatomic, assign) CGFloat interitemSpacing;

// header 高度(默认0.0)

@property (nonatomic, assign) CGFloat headerViewHeight;

// footer 高度(默认0.0)

@property (nonatomic, assign) CGFloat footerViewHeight;

// item 高度 (默认30)

@property (nonatomic, assign) CGFloat itemHeight;

// footer 边距缩进(默认UIEdgeInsetsZero)

@property (nonatomic, assign) UIEdgeInsets footerInset;

// header 边距缩进(默认UIEdgeInsetsZero)

@property (nonatomic, assign) UIEdgeInsets headerInset;

// item 边距缩进(默认UIEdgeInsetsZero)

@property (nonatomic, assign) UIEdgeInsets itemInset;

@end

NS_ASSUME_NONNULL_END







//============================================.m==========================

//

//  TopBarLayout.m

//  LOLADFNeverGiveUp

//

//  Created by 曹老师 on 2018/12/17.

//  Copyright © 2018  All rights reserved.

//

#import "ZoneImageFlowLayout.h"

@interface ZoneImageFlowLayout()

/** 总的布局对象数组,包括item,sectionHeader,footerHeader */

@property (nonatomic, strong) NSMutableArray *attributesArray;

/** item的布局对象数组 */

@property (nonatomic, strong) NSMutableArray *itemsattributes;

/** header的布局对象数组 */

@property(nonatomic,strong)NSMutableArray*headerAttributes;

/** footer的布局对象数组 */

@property(nonatomic,strong)NSMutableArray*footerAttributes;

/** 计算 collectionview 的内容高度 */

@property (nonatomic, assign) CGFloat contentHeight;

/** 计算 collectionview 的内容宽度 */

@property (nonatomic, assign) CGFloat contentWidth;

/** collectionview 自身的宽度 */

@property (nonatomic, assign) CGFloat viewWidth;

@end

@implementationZoneImageFlowLayout

#pragma mark - initialize

- (instancetype)init {

    if(self= [superinit]) {


        //设置间距的默认值

        self.headerViewHeight = 0.0;

        self.footerViewHeight = 0.0;

        self.interitemSpacing = 4.0;

        self.lineSpacing=4.0;

        self.itemHeight= (kScreenWidth-48) /3;

        self.itemInset = UIEdgeInsetsZero;

        self.headerInset = UIEdgeInsetsZero;

        self.footerInset = UIEdgeInsetsZero;

    }

    return self;

}

/** 1、当collectionView布局item时 第一个执行的方法 */

- (void)prepareLayout {

    /** 重写layout中的方法 首先必须调用父类 */

    [superprepareLayout];


    self.viewWidth =self.collectionView.bounds.size.width -self.itemInset.left -self.itemInset.right;

    //所有内容的布局属性数组

    self.attributesArray = [NSMutableArray array];


    //item的数据模型是2原数组,就是第一层数组包含的是section,第二层是每个section包含的item

    self.itemsattributes = [NSMutableArray array];

    //记录 collectionview 的内容高度

    self.contentHeight =0.0;

    self.contentWidth =0.0;


    /** 获取collectionView 中的item的个数 */

    NSInteger sectionCount = [self.collectionView numberOfSections];

    /** 遍历得到每个item 设置位置信息 */

    for(NSInteger i =0; i < sectionCount; i++) {


        NSInteger itemCount = [self.collectionView numberOfItemsInSection:i];

        for(NSInteger j =0; j < itemCount; j++) {

            [selfsetItemFrameWithIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];


            if( (i == sectionCount -1) && ( j == itemCount -1) ) {

                //这里是当最后一个 item 的 layoutAttributes 设置完成后如果有设置 footer 就要把 footer 添加到所有 layoutAttributes 数组

//                if ( [(NSObject *)self.delegate respondsToSelector:@selector(collectionViewDynamicFooterSizeWithIndexPath:)] ) {

//

//                    //获取最后一个 item 的 layoutAttributes

//                    UICollectionViewLayoutAttributes *lastAttributes = self.attributesArray.lastObject;

//                    //添加 footer 的 layoutAttributes

//                    [self makeFooterAttributesWithLastItemAttributes:lastAttributes];

//

//                    // 获取新添加的 footer 的 layoutAttributes

//                    UICollectionViewLayoutAttributes *footerAttributes = self.footerAttributes.lastObject;

//                    //计算总高度

//                    self.contentHeight = CGRectGetMaxY(footerAttributes.frame) + self.itemInset.bottom;

//                }

            }

        }

    }

}

- (void)setItemFrameWithIndexPath:(NSIndexPath *)indexPath {


    //这里主要是设置一下 item 的初始 frame

    CGFloat x =0.0;

    CGFloat y =0.0;

    CGFloat width = (kScreenWidth -48) /3;



    // 获取数组最后一个 layoutAttributes, 这样方便计算 frame 和 判断是否需要计算新的 section

    UICollectionViewLayoutAttributes *lastAttributes =self.attributesArray.lastObject;


    if( lastAttributes ) {

        //如果数组有值代表不是设置第一个item

        if( lastAttributes.indexPath.section == indexPath.section ) {

            //同一组

            if( (CGRectGetMaxX(lastAttributes.frame) +self.interitemSpacing + width >self.viewWidth) &&

                (self.scrollDirection == UICollectionViewScrollDirectionVertical)) {

                //需要换行

                x =self.itemInset.left;

                y = CGRectGetMaxY(lastAttributes.frame) +self.lineSpacing;

            }else{

                //不需要换行

                x = CGRectGetMaxX(lastAttributes.frame) +self.interitemSpacing;

                y = CGRectGetMinY(lastAttributes.frame);

            }

        }else{

            //不同一组

            //添加 footer 的布局,内部会判断是否需要添加

            [selfmakeFooterAttributesWithLastItemAttributes:lastAttributes];


            //添加一个新的 section 数组

            [self.itemsattributes addObject:[NSMutableArray array]];


            //这里重新获取最后一个 layoutAttributes 是因为如果加入了 footer 总的 layoutAttributes就会改变

            lastAttributes =self.attributesArray.lastObject;


            //添加 header 的布局,内部会判断是否需要添加

            [selfmakeHeaderAttributesWithIndexPath:indexPath lastItemAttributes:lastAttributes];


            //设置新的 section 的第一个 item 的 frame

            x =self.itemInset.left;

            y = CGRectGetMaxY(lastAttributes.frame) +self.lineSpacing *2+self.headerViewHeight;

        }

    }else{

        //这里是设置第一个section的item

        [self.itemsattributes addObject:[NSMutableArray array]];


        //添加 header 的布局,内部会判断是否需要添加

        [selfmakeHeaderAttributesWithIndexPath:indexPath lastItemAttributes:lastAttributes];


        //这里判断是否有 header, 如果有就获取最后一个 layoutAttributes

        if(self.headerAttributes.count ) {

            lastAttributes =self.attributesArray.lastObject;

        }


        //设置新的 section 的第一个 item 的 frame

        x =self.itemInset.left;

        y = lastAttributes.size.height;

    }


    //设置每一个 item 的 frame

    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    /** 添加frame */

    attributes.frame = CGRectMake(x, y, width, width);

    self.contentHeight = CGRectGetMaxY(attributes.frame) +self.lineSpacing;

    self.contentWidth = CGRectGetMaxX(attributes.frame) +self.interitemSpacing;

    /** 保存在数组中 */

    [self.itemsattributes[indexPath.section] addObject:attributes];

    [self.attributesArray addObject:attributes];

}

#pragma mark - New Header Or Footer

- (void)makeHeaderAttributesWithIndexPath:(NSIndexPath *)indexPath lastItemAttributes:(UICollectionViewLayoutAttributes *)attributes {

    //设置第一个section的header

    UICollectionViewLayoutAttributes *headerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];


    CGFloat y = (attributes)?CGRectGetMaxY(attributes.frame) +self.lineSpacing:self.itemInset.top;

    CGFloat headerWidth =  0.0;

    CGFloat headerHeight =  0.0;


//    if ( [(NSObject *)self.delegate respondsToSelector:@selector(collectionViewDynamicHeaderSizeWithIndexPath:)] ) {

//        CGSize size = [self.delegate collectionViewDynamicHeaderSizeWithIndexPath:indexPath];

//

//        headerWidth = size.width;

//        headerHeight = size.height;

//    }else {

//        headerWidth = kScreenWidth - self.headerInset.left - self.headerInset.right;

//        headerHeight = self.headerViewHeight;

//    }


    if( headerHeight >0.0) {

        headerAttributes.frame = CGRectMake(self.headerInset.left, y, headerWidth, headerHeight);


        [self.headerAttributes addObject:headerAttributes];

        [self.attributesArray addObject:headerAttributes];

    }

}

- (void)makeFooterAttributesWithLastItemAttributes:(UICollectionViewLayoutAttributes*)attributes {


    UICollectionViewLayoutAttributes *footerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:attributes.indexPath];


    CGFloatfooterWidth =  0.0;

    CGFloatfooterHeight =  0.0;

//    if ( [(NSObject *)self.delegate respondsToSelector:@selector(collectionViewDynamicFooterSizeWithIndexPath:)] ) {

//

//        CGSize size = [self.delegate collectionViewDynamicFooterSizeWithIndexPath:attributes.indexPath];

//        footerWidth = size.width;

//        footerHeight = size.height;

//    } else {

//        footerWidth = kScreenWidth - self.footerInset.left - self.footerInset.right;

//        footerHeight = self.footerViewHeight;

//    }


    if( footerHeight >0) {

        footerAttributes.frame=CGRectMake(self.footerInset.left,CGRectGetMaxY(attributes.frame) +self.lineSpacing, footerWidth, footerHeight);

        [self.footerAttributesaddObject:footerAttributes];

        [self.attributesArrayaddObject:footerAttributes];

    }


}

#pragma mark - 布局

//这个是返回所有 header, footer, item 属性的回调方法, 一定要实现

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    return self.attributesArray;

}

- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath {

    returnself.itemsattributes[indexPath.section][indexPath.item];

}

- (UICollectionViewLayoutAttributes*)layoutAttributesForSupplementaryViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath*)indexPath {

    if ( [elementKind isEqual: UICollectionElementKindSectionHeader] ) {

        returnself.headerAttributes[indexPath.section];

    }else{

        returnself.footerAttributes[indexPath.section];

    }

}

/** 4、设置滚动范围 */

// 这里可以处理 uicollectionview 内容不够屏幕高度不能滑动的问题,只要把 contentsize.height 设置成比屏幕高度大就可以了

- (CGSize)collectionViewContentSize {


    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) {

        return CGSizeMake(self.contentWidth + self.itemInset.right, 0.0);

    }else{

        return CGSizeMake(0.0, self.contentHeight + self.itemInset.bottom);

    }

}

#pragma mark - LazyLoad

// header 的布局属性数组

- (NSMutableArray*)headerAttributes {

    if ( !_headerAttributes ) {

        _headerAttributes = [NSMutableArray array];

    }

    return _headerAttributes;

}

// footer 的布局属性数组

- (NSMutableArray*)footerAttributes {

    if ( !_footerAttributes ) {

        _footerAttributes = [NSMutableArray array];

    }

    return _footerAttributes;

}

@end

你可能感兴趣的:(自定义的UICollectionViewFlowLayout)