UICollectionView作历史记录

效果图:参考:https://github.com/celiaDeveloper/XDEShop,这个不能知足需求,因此我改了下git

layout:github

ReceiveHomeVC.h数组

#import <UIKit/UIKit.h>

@protocol HorizontalCollectionLayoutDelegate <NSObject>

@optional
// 用协议传回 item 的内容,用于计算 item 宽度
- (NSString *)collectionViewItemSizeWithIndexPath:(NSIndexPath *)indexPath;
// 用协议传回 headerSize 的 size
- (CGSize)collectionViewDynamicHeaderSizeWithIndexPath:(NSIndexPath *)indexPath;
// 用协议传回 footerSize 的 size
- (CGSize)collectionViewDynamicFooterSizeWithIndexPath:(NSIndexPath *)indexPath;
@end

@interface XDCollectionHeaderLayout : 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;
// item Label Font(默认系统字体15)
@property (nonatomic, copy) UIFont *labelFont;

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


@end

 XDCollectionHeaderLayout.m布局

#import "XDCollectionHeaderLayout.h"
#define HPScreenWidth       [UIScreen mainScreen].bounds.size.width
@interface XDCollectionHeaderLayout()

/** 总的布局对象数组,包括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 viewWidth;


@end

@implementation XDCollectionHeaderLayout

#pragma mark - initialize
- (instancetype)init {
    if (self = [super init]) {
        
        //设置间距的默认值
        self.headerViewHeight = 0.0;
        self.footerViewHeight = 0.0;
        self.interitemSpacing = 4.0;
        self.lineSpacing = 4.0;
        self.itemHeight = 30.0;
        self.itemInset = UIEdgeInsetsZero;
        self.headerInset = UIEdgeInsetsZero;
        self.footerInset = UIEdgeInsetsZero;
        self.labelFont = [UIFont systemFontOfSize:15.0];
    }
    return self;
}

/** 一、当collectionView布局item时 第一个执行的方法 */
- (void)prepareLayout {
    /** 重写layout中的方法 首先必须调用父类 */
    [super prepareLayout];
    
    self.viewWidth = HPScreenWidth - self.itemInset.left - self.itemInset.right;
    
    NSLog(@"-----------------:%f  %f  %f",HPScreenWidth ,self.itemInset.left ,self.itemInset.right);
    //全部内容的布局属性数组
    self.attributesArray = [NSMutableArray array];
    
    //item的数据模型是2原数组,就是第一层数组包含的是section,第二层是每一个section包含的item
    self.itemsattributes = [NSMutableArray array];
    //记录 collectionview 的内容高度
    self.contentHeight = 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++) {
            [self setItemFrameWithIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];
            
            if ( (i == sectionCount - 1) && ( j == itemCount - 1) ) {
                //这里是当最后一个 item 的 layoutAttributes 设置完成后若是有设置 footer 就要把 footer 添加到全部 layoutAttributes 数组
                if ( [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 = [self countItemSizeWidthWithIndexPath:indexPath];
    CGFloat height = [self countItemSizeWithIndexPath:indexPath];
    
    // 获取数组最后一个 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 ) {
                //须要换行
                x = self.itemInset.left + self.lineSpacing;
                y = CGRectGetMaxY(lastAttributes.frame) + self.lineSpacing;
            }else {
                //不须要换行
                x = CGRectGetMaxX(lastAttributes.frame) + self.interitemSpacing;
                y = CGRectGetMinY(lastAttributes.frame);
            }
        }else {
            //不一样一组
            //添加 footer 的布局,内部会判断是否须要添加
            [self makeFooterAttributesWithLastItemAttributes:lastAttributes];
            
            //添加一个新的 section 数组
            [self.itemsattributes addObject:[NSMutableArray array]];
            
            //这里从新获取最后一个 layoutAttributes 是由于若是加入了 footer 总的 layoutAttributes就会改变
            lastAttributes = self.attributesArray.lastObject;
            
            //添加 header 的布局,内部会判断是否须要添加
            [self makeHeaderAttributesWithIndexPath:indexPath lastItemAttributes:lastAttributes];
            
            //设置新的 section 的第一个 item 的 frame
            x = self.itemInset.left + self.lineSpacing;
            y = CGRectGetMaxY(lastAttributes.frame) + self.lineSpacing * 2 + self.headerViewHeight;
        }
    }else {
        //这里是设置第一个section的item
        [self.itemsattributes addObject:[NSMutableArray array]];
        
        //添加 header 的布局,内部会判断是否须要添加
        [self makeHeaderAttributesWithIndexPath:indexPath lastItemAttributes:lastAttributes];
        
        //这里判断是否有 header, 若是有就获取最后一个 layoutAttributes
        if ( self.headerAttributes.count ) {
            lastAttributes = self.attributesArray.lastObject;
        }
        
        //设置新的 section 的第一个 item 的 frame
        x = self.itemInset.left + self.lineSpacing;
        y = self.lineSpacing + lastAttributes.size.height;
    }
    
    //设置每个 item 的 frame
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    /** 添加frame */
    if(width >= self.viewWidth-self.interitemSpacing*2){
        attributes.frame = CGRectMake(x, y, self.viewWidth-self.interitemSpacing*2, height);
    }else{
        attributes.frame = CGRectMake(x, y, width, self.itemHeight);
    }
    
    self.contentHeight = CGRectGetMaxY(attributes.frame) + self.lineSpacing;
    /** 保存在数组中 */
    [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 ( [self.delegate respondsToSelector:@selector(collectionViewDynamicHeaderSizeWithIndexPath:)] ) {
        CGSize size = [self.delegate collectionViewDynamicHeaderSizeWithIndexPath:indexPath];
        
        headerWidth = size.width;
        headerHeight = size.height;
    }else {
        headerWidth = HPScreenWidth - 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];
    
    CGFloat footerWidth =  0.0;
    CGFloat footerHeight =  0.0;
    if ( [self.delegate respondsToSelector:@selector(collectionViewDynamicFooterSizeWithIndexPath:)] ) {
        
        CGSize size = [self.delegate collectionViewDynamicFooterSizeWithIndexPath:attributes.indexPath];
        footerWidth = size.width;
        footerHeight = size.height;
    } else {
        footerWidth = HPScreenWidth - 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.footerAttributes addObject:footerAttributes];
        [self.attributesArray addObject:footerAttributes];
    }
    
}

#pragma mark - 布局
//这个是返回全部 header, footer, item 属性的回调方法, 必定要实现
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return self.attributesArray;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    return self.itemsattributes[indexPath.section][indexPath.item];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
    if ( [elementKind isEqual: UICollectionElementKindSectionHeader] ) {
        return self.headerAttributes[indexPath.section];
    }else {
        return self.footerAttributes[indexPath.section];
    }
}

/** 四、设置滚动范围 */
// 这里能够处理 uicollectionview 内容不够屏幕高度不能滑动的问题,只要把 contentsize.height 设置成比屏幕高度大就能够了
- (CGSize)collectionViewContentSize {
    
    return CGSizeMake(0.0, self.contentHeight + self.itemInset.bottom);
}

- (CGFloat)countItemSizeWithIndexPath:(NSIndexPath *)indexPath {
    NSString *content = [self.delegate collectionViewItemSizeWithIndexPath:indexPath];
    CGSize size =   [content boundingRectWithSize:CGSizeMake(self.viewWidth-self.interitemSpacing*2, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:self.labelFont} context: nil].size;
    return MAX(size.height, self.itemHeight);
}
- (CGFloat)countItemSizeWidthWithIndexPath:(NSIndexPath *)indexPath{
    NSString *content = [self.delegate collectionViewItemSizeWithIndexPath:indexPath];
    CGSize size =   [content boundingRectWithSize:CGSizeMake(MAXFLOAT, 10) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:self.labelFont} context: nil].size;
    NSLog(@"==========================:%@",self.labelFont);
    
    return size.width+self.interitemSpacing;
}


#pragma mark - lazy
// header 的布局属性数组
- (NSMutableArray *)headerAttributes {
    if ( !_headerAttributes ) {
        _headerAttributes = [NSMutableArray array];
    }
    return _headerAttributes;
}

// footer 的布局属性数组
- (NSMutableArray *)footerAttributes {
    if ( !_footerAttributes ) {
        _footerAttributes = [NSMutableArray array];
    }
    return _footerAttributes;
}


@end

 使用方法:字体

#import "XDCollectionHeaderLayout.h"
#import "Cell.h"
#import <Masonry/Masonry.h>
@interface ViewController ()<HorizontalCollectionLayoutDelegate,UICollectionViewDataSource>

@property(nonatomic,strong)UICollectionView *collectionView;
@property(nonatomic,strong)NSArray *dataArr;

@end
@implementation ViewController
-(NSArray *)dataArr{
    if(!_dataArr){
        _dataArr = @[@"需求1",@"需求需求需求需求需求2",@"需求需求需求3",@"需求需求4",@"需求需求需求需求需求需求需求需求需求需求",@"需求需求需求需求需求需求需求需求需求需求需求需求需求需求需求需求需求5",@"需求6",@"需求7",@"需求8",];
    }
    return _dataArr;
}

- (UICollectionView *)collectionView {
    if (!_collectionView) {
        XDCollectionHeaderLayout *layout = [XDCollectionHeaderLayout new];
        layout.labelFont = [UIFont systemFontOfSize:18];
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
        //自定义layout初始化
        layout.delegate = self;
        layout.lineSpacing = 8.0;
        layout.interitemSpacing = 10;
        layout.headerViewHeight = 35;
        layout.footerViewHeight = 5;
        layout.itemInset = UIEdgeInsetsMake(0, 10, 0, 10);
        
        _collectionView.dataSource = self;
        _collectionView.alwaysBounceVertical = YES;
        [_collectionView registerClass:[Cell class] forCellWithReuseIdentifier:@"Cell"];//cell
        _collectionView.backgroundColor = [UIColor whiteColor];
        
    }
    return _collectionView;
}
- (void)viewDidLoad {
    [super viewDidLoad];
  
    [self.view addSubview:self.collectionView];
    [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.offset(100);
        make.left.right.bottom.offset(0);
    }];
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.dataArr.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.str = self.dataArr[indexPath.row];
    return cell;
}

// 用协议传回 item 的内容,用于计算 item 宽度
- (NSString *)collectionViewItemSizeWithIndexPath:(NSIndexPath *)indexPath{
    return self.dataArr[indexPath.row];
}


@end

 cell的内容很简单,就一个labelui

#import "Cell.h"
#import <Masonry/Masonry.h>
@interface Cell ()
@property (nonatomic, strong) UILabel *attLabel;

@end

@implementation Cell

-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
        _attLabel = [[UILabel alloc] init];
        _attLabel.textAlignment = NSTextAlignmentCenter;
        _attLabel.font = [UIFont systemFontOfSize:18];
        [self.contentView addSubview:_attLabel];
        _attLabel.numberOfLines = 0;
        [_attLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(self);
        }];
        self.contentView.backgroundColor = [UIColor redColor];
    }
    return self;
}

-(void)setStr:(NSString *)str{
    _str = str;
    _attLabel.text = str;
}

@end

 

 

 

不懂的话欢迎提问 atom

相关文章
相关标签/搜索