UICollectionView的基本使用

瀑布流(WaterFlow)是iPad项目开发过程中的常见布局,有关于瀑布流(WaterFlow)的实现方式:在UICollectionView未出现之前,瀑布流的实现多半是采用UIScrollView或是UITableView。对于我们这种用惯了表视图的人来说,UICollectionView倒略显陌生。本文主要是介绍如何采用纯代码的方式利用UICollectionView实现带上下拉刷新的瀑布流式(WaterFlow)布局。

如何简单的使用UICollectionView 和如何添加headerView 让他也拥有类似于tableview的headerView的效果

一、UICollectionView集成上下拉刷新

(1)简单的介绍UICollectionView

UICollectionView和UITableView很相似,使用它时需要相应的设置DataSource(UICollectionViewDataSoure)数据源协议和Delegate(UICollectionViewDelegate)事件协议,并实现相应的协议方法。它与UITableView不同的地方是它有UICollectionViewLayout,可以通过它的子类来定制布局。系统默认的布局方式是UICollectionViewDelegateFlowLayout,实现相应的协议(UICollectionViewDelegateFlowLayout)方法,便可达到默认布局效果。

UICollectionViewCell与UITableViewCell的使用也差不多,一样的有ReuseIdentifier,可以复用。使用UICollectionViewCell时,需要先注册,然后再出列使用。

static NSString * const YYPShopId = @"shop";
static NSString * const collectionHeaderViewID = @"collectionHeaderView";

@property (nonatomic, weak) UICollectionView *collectView;

    // 创建布局
    YYPWaterflowLayout *layout = [[YYPWaterflowLayout alloc] init];
    layout.delegate = self;
    
    // 创建CollectionView
    UICollectionView *collectView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.headerView.frame), kContentViewWidth, self.view.height - CGRectGetMaxY(self.headerView.frame)) collectionViewLayout:layout];
    collectView.backgroundColor = [UIColor whiteColor];
    collectView.dataSource = self;
    collectView.delegate = self;
    [collectView addSubview:self.headerView];    
    [self.view addSubview:collectView];
    self.collectView = collectView;
    
    // 注册头视图:kind代表Supplementary视图类型,UICollectionElementKindSectionHeader表示组头
    [collectView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:collectionHeaderViewID];
    // 注册
    [collectView registerNib:[UINib nibWithNibName:NSStringFromClass([XMGShopCell class]) bundle:nil] forCellWithReuseIdentifier:YYPShopId];

(2)集成上下拉刷新方式

方法1:
一般UITableView添加上下拉刷新的方式,是把下拉或者上拉刷新视图的UI放置UITableView的表头属性(tableHeaderView)或者表尾属性(tableFooterView)。但是,在UICollectionView中没有这两个属性。那么我们该如何来集成上下拉刷新呢?这就得说到
UICollectionView布局中的三种显示内容视图:Cells、Supplementary views、Decoration views。我们一般是把上下拉刷新的UI放置在Supplementary views(官方解释:它能显示数据但是不同于Cells。不像Cell,Supplementary views是不能被用户选定的。相反,你可以用它去实现类似于给一个指定的section或者整个CollectionView添加页眉headerView和页脚视图footerView这样的功能)上面,其实,Supplementary views就和TableView的section头视图和尾视图差不多。但是用法却大不相同。在自定义瀑布流布局中一定要把它也计算进去,不然显示就会有异常。

注册头视图headerView和尾视图footerView单元格类型
//注册头视图headerView:kind代表Supplementary视图类型,UICollectionElementKindSectionHeader表示组头
[self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"collectionHeaderView"];

//注册尾视图footerView:UICollectionElementKindSectionHeader表示组尾
 [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"collectionFooterView"];
实现自定义组视图viewForSupplementaryElementOfKind代理方法
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {

    //kind代表Supplementary视图类型:头或尾视图(header OR footer)

    if([kind isEqual:UICollectionElementKindSectionHeader]) {
        UICollectionReusableView *collectionHeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"bGCollectionHeaderView" forIndexPath:indexPath];

        //初始化下拉刷新或者自定义页眉视图UI界面headerView
        ...

        return collectionHeaderView;
    } else if([kind isEqual:UICollectionElementKindSectionFooter]) {
        UICollectionReusableView *collectionFooterView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"bGCollectionFooterView" forIndexPath:indexPath];

        //初始化上拉刷新或者自定义页脚视图UI界面footerView
        ...

        return collectionFooterView;
    }

    return nil;
}

完成以上两步,集成上下拉刷新的功能总算完成了。接下来你可以运行一下工程,看结果是否符合预期。如果你CollectionView使用的是系统的UICollectionViewFlowLayout布局,一定要返回组头或者组尾的高度,否则就会出现组头或者组尾无法显示(本文的下拉刷新视图所在的组头不返回高度不受影响,是因为它的纵坐标(y值)本来就是负数)。另外,还有一点需要注意的是,如果你的CollectionView中存在多组,最好是把上下拉刷新所在的组头或者组尾与其他组(section)的组头或组尾分开,相应的需要多注册一个组头或者组尾视图,分情况进行判断。

方法2:使用MJRefresh.h来快速集成上下拉刷新方式,这种比较方便快捷

- (void)setupRefresh {
    
    self.collectView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewShops)];
    self.collectView.mj_header.automaticallyChangeAlpha = YES;
    [self.collectView.mj_header beginRefreshing];
    
    self.collectView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreShops)];
    self.collectView.mj_footer.hidden = YES;
}

- (void)loadNewShops {
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 1.读取数据 2.移除所有数据 3.添加数据到数组
        // NSArray *shops = [YYPShop objectArrayWithFilename:@"1.plist"];
      //  [self.shops removeAllObjects];
      //  [self.shops addObjectsFromArray:shops];
        
        // 刷新数据
        [self.collectView reloadData];
        // 结束顶部刷新控件
        [self.collectView.mj_header endRefreshing];
    });
}

- (void)loadMoreShops {
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 1.读取数据 2.添加数据到数组
      //  NSArray *shops = [YYPShop objectArrayWithFilename:@"1.plist"];
      //  [self.shops addObjectsFromArray:shops];
        
        // 刷新数据
        [self.collectView reloadData];
        // 结束尾部刷新控件
        [self.collectView.mj_footer endRefreshing];
    });
}

二、自定义瀑布流(WaterFlow)式布局

(1)简单的介绍UICollectionView布局形式

一般来讲,UICollectionView的布局形式分为两种:

布局与内容独立,其布局不需要根据显示的单元格内容来计算。Cell的显示顺序与内容顺序一致。Cell排满一行则换至下一行继续排列。系统的UICollectionViewFlowLayout就是这样。

布局需要计算内容,UICollectionViewFlowLayout系统布局在换行的时候,由于每个item高度不同,从而导致布局错乱,无法满足你的需求。所以,这个时候你需要计算每个item的高度,最终用来确定item显示的位置。

从上述内容我们可以看出,一般如果布局需要计算内容的时候,我们就不应该直接使用UICollectionViewFlowLayout布局了,而是应该子类化一个UICollectionViewLayout,从而达到私人定制的目的。

UICollectionViewLayout.h:

//  Created by qtx on 16/8/8.
//  Copyright © 2016年 zhangli. All rights reserved.
//  瀑布流布局

#import <UIKit/UIKit.h>
@class YYPWaterflowLayout;

@protocol YYPWaterflowLayoutDelegate <NSObject>

@required
- (CGFloat)waterflowLayout:(XMGWaterflowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;

@optional
- (CGFloat)columnCountInWaterflowLayout:(YYPWaterflowLayout *)waterflowLayout;
- (CGFloat)columnMarginInWaterflowLayout:(YYPWaterflowLayout *)waterflowLayout;
- (CGFloat)rowMarginInWaterflowLayout:(YYPWaterflowLayout *)waterflowLayout;
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(YYPWaterflowLayout *)waterflowLayout;

@end


@interface YYPWaterflowLayout : UICollectionViewFlowLayout

/** 代理 */
@property (nonatomic, weak) id<YYPWaterflowLayoutDelegate> delegate;

@end

UICollectionViewLayout.m:

//  Created by qtx on 16/8/8.
//  Copyright © 2016年 zhangli. All rights reserved.
//

#import "YYPWaterflowLayout.h"

/** 默认的列数 */
static const NSInteger YYPDefaultColumnCount = 2;
/** 每一列之间的间距 */
static const CGFloat YYPDefaultColumnMargin = 10;
/** 每一行之间的间距 */
static const CGFloat YYPDefaultRowMargin = 10;
/** 边缘间距 */
static const UIEdgeInsets YYPDefaultEdgeInsets = {10, 10, 10, 10};


@interface YYPWaterflowLayout()

/** 存放所有cell的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
/** 存放所有列的当前高度 */
@property (nonatomic, strong) NSMutableArray *columnHeights;
/** 内容的高度 */
@property (nonatomic, assign) CGFloat contentHeight;

- (CGFloat)rowMargin;
- (CGFloat)columnMargin;
- (NSInteger)columnCount;
- (UIEdgeInsets)edgeInsets;

@end

@implementation YYPWaterflowLayout

#pragma mark - 常见数据处理

- (CGFloat)rowMargin {
    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
        return [self.delegate rowMarginInWaterflowLayout:self];
    } else {
        return YYPDefaultRowMargin;
    }
}

- (CGFloat)columnMargin {
    if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) {
        return [self.delegate columnMarginInWaterflowLayout:self];
    } else {
        return YYPDefaultColumnMargin;
    }
}

- (NSInteger)columnCount {
    if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) {
        return [self.delegate columnCountInWaterflowLayout:self];
    } else {
        return YYPDefaultColumnCount;
    }
}

- (UIEdgeInsets)edgeInsets {
    if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {
        return [self.delegate edgeInsetsInWaterflowLayout:self];
    } else {
        return YYPDefaultEdgeInsets;
    }
}

#pragma mark - 懒加载

- (NSMutableArray *)columnHeights {
    if (!_columnHeights) {
        _columnHeights = [NSMutableArray array];
    }
    return _columnHeights;
}

- (NSMutableArray *)attrsArray {
    if (!_attrsArray) {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

/**
 * 初始化
 */
- (void)prepareLayout {
    [super prepareLayout];
    
    self.contentHeight = 0;
    
    // 清除以前计算的所有高度
    [self.columnHeights removeAllObjects];
    for (NSInteger i = 0; i < self.columnCount; i++) {
        [self.columnHeights addObject:@(self.edgeInsets.top)];
    }
    
    // 清除之前所有的布局属性
    [self.attrsArray removeAllObjects];
    // 开始创建每一个cell对应的布局属性
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger i = 0; i < count; i++) {
        // 创建位置
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        // 获取indexPath位置cell对应的布局属性
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attrsArray addObject:attrs];
    }
}

/**
 * 决定cell的排布
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    return self.attrsArray;
}

/**
 * 返回indexPath位置cell对应的布局属性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    // 创建布局属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    // collectionView的宽度
    CGFloat collectionViewW = self.collectionView.frame.size.width;
    
    // 设置布局属性的frame
    CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;
    CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
    
    // 找出高度最短的那一列
    NSInteger destColumn = 0;
    CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < self.columnCount; i++) {
        // 取得第i列的高度
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];
        
        if (minColumnHeight > columnHeight) {
            minColumnHeight = columnHeight;
            destColumn = i;
        }
    }
    
    CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
    CGFloat y = minColumnHeight;
    if (y != self.edgeInsets.top) {
        y += self.rowMargin;
    }
    attrs.frame = CGRectMake(x, y, w, h);
    
    // 更新最短那列的高度
    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
    
    // 记录内容的高度
    CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
    if (self.contentHeight < columnHeight) {
        self.contentHeight = columnHeight;
    }
    return attrs;
}

- (CGSize)collectionViewContentSize {
//    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
//    for (NSInteger i = 1; i < self.columnCount; i++) {
//        // 取得第i列的高度
//        CGFloat columnHeight = [self.columnHeights[i] doubleValue];
//
//        if (maxColumnHeight < columnHeight) {
//            maxColumnHeight = columnHeight;
//        }
//    }
    return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
}

@end

(2)创建自定义布局

官方文档中有提到,在布局的过程中,CollectionView会调用布局对象的具体方法。这些方法有机会使你计算这些项的位置以及给CollectionView提供它所需要的主要信息。其他的方法也有可能会被调用,但是在布局的过程中这些方法总是会按照以下的顺序调用:

1.使用”prepareLayout”方法去执行一些CollectionView所需要的布局信息的预先计算操作。
2.使用”collectionViewContentSize”方法去返回根据你最初计算的整个内容区域的总体大小。
3.使用”layoutAttributesForElementsInRect:”方法来返回指定区域的单元格与视图属性。

重写”prepareLayout”方法计算布局信息。
- (void)prepareLayout{
    [super prepareLayout];

    //计算每个显示项的宽度,horizontalItemSpacing代表项与项之间的水平间隔, 
    columnNum代表总列数,contentInset代表上下左右的内填充。详见下图说明

    self.itemWidth = (self.collectionView.frame.size.width - (self.horizontalItemSpacing * (self.columnNum - 1)) - self.contentInset.left - self.contentInset.right) / self.columnNum;

    //如有下拉刷新需求的则需要提供头视图(Supplementary view)布局属性。
   
    if(self.headerHeight > 0){

    //通过layoutAttributesForSupplementaryViewOfKind:withIndexPath:方法来创建Supplementary视图布局属性对象,kind用来区分Supplementary视图类型(header或者footer)。

        self.headerLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];

        //修改布局参数frame属性

        self.headerLayoutAttributes.frame = CGRectMake(0, 0, self.collectionView.frame.size.width, self.headerHeight);
    }

    //初始化保存布局属性的字典
    NSMutableDictionary *cellLayoutInfoDic = [NSMutableDictionary dictionary];
    //初始化列高数组
    NSMutableArray *columnInfoArray = [self columnInfoArray];
    NSInteger numSections = [self.collectionView numberOfSections];
    for(NSInteger section = 0; section < numSections; section++)  {
        NSInteger numItems = [self.collectionView numberOfItemsInSection:section];
        for(NSInteger item = 0; item < numItems; item++){
            //获取列高最小的model,以它的高作为y坐标
            ZLWaterFlowModel *firstModel = columnInfoArray.firstObject;
            CGFloat y = firstModel.height;
            CGFloat x = self.contentInset.left + (self.horizontalItemSpacing + self.itemWidth) * firstModel.column;

            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];

            //通过代理方法传入对应item的高度。

            CGFloat itemHeight = [((id<BGWaterFlowLayoutDelegate>)self.collectionView.delegate) collectionView:self.collectionView layout:self heightForItemAtIndexPath:indexPath];

            //通过layoutAttributesForCellWithIndexPath:方法来创建cell的布局属性对象。

            UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
            //计算item的布局属性
            itemAttributes.frame = CGRectMake(x, y+self.contentInset.top+self.headerHeight, self.itemWidth, itemHeight);
            //计算当前列高,verticalItemSpacing代表项与项之间的垂直间隔。
            firstModel.height += (itemHeight + self.verticalItemSpacing);
            //保存新的列高,并进行排序:从后往前查找,查找到高度比它小的对象,就插入到该对象之后。
            [self sortArrayByHeight:columnInfoArray];

     //保存计算好的item布局属性
     cellLayoutInfoDic[indexPath] = itemAttributes;
        }
    }

    //保存局部布局属性字典到全局字典中
    self.cellLayoutInfoDic = [cellLayoutInfoDic copy];

    //按照前面的排序逻辑,列高数组中的最后一个元素,就是高度最大的一列。
    ZLWaterFlowModel *lastModel = columnInfoArray.lastObject;

    //如有上拉刷新需求的则需要提供尾视图布局属性。

    if(self.footerHeight > 0){
        self.footerLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
        //计算尾视图的布局属性
        self.footerLayoutAttributes.frame = CGRectMake(0, lastModel.height+self.headerHeight+self.contentInset.top+self.contentInset.bottom, self.collectionView.frame.size.width, self.footerHeight);
    }

    //直接计算出collectionView的contentSize
    self.contentSize = CGSizeMake(self.collectionView.frame.size.width, lastModel.height+self.headerHeight+self.contentInset.top+self.contentInset.bottom+self.footerHeight);
}

重写”collectionViewContentSize”方法返回collectionView的内容高度。

- (CGSize)collectionViewContentSize{
    //返回计算好的collectionView内容高度
    return self.contentSize;
}

重写”layoutAttributesForElementsInRect”方法返回指定矩形区域中的cell或者其他类型视图布局属性。

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

    //返回一组已经计算好的布局属性。

    NSMutableArray *attributesArrs = [NSMutableArray array];
    [self.cellLayoutInfoDic enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
                                                                UICollectionViewLayoutAttributes *attributes,
                                                                BOOL *stop) { 
        //遍历布局属性保存字典,添加布局属性至数组中                                                       
        if (CGRectIntersectsRect(rect, attributes.frame)) {
            [attributesArrs addObject:attributes];
        }
    }];

    //如果有头视图或者尾视图则需要添加头视图与尾视图的布局属性

    if (self.headerLayoutAttributes && CGRectIntersectsRect(rect, self.headerLayoutAttributes.frame)) {
        [attributesArrs addObject:self.headerLayoutAttributes];
    }


    if (self.footerLayoutAttributes && CGRectIntersectsRect(rect, self.footerLayoutAttributes.frame)) {
        [attributesArrs addObject:self.footerLayoutAttributes];
    }

    return attributesArrs;
}
下面执行代理方法 跟tableview 一样的
#pragma mark -
#pragma mark collectionViewDelegate
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    //section数量
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    //数据数量
}
//设置元素大小
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    return (返回一个Item的 宽高)
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//此处就是自定义好的cell
}


-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
//UICollectionView被选中时调用的方法
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    ZLHeaderView *headReusableView;
//此处是headerView
    if (kind == UICollectionElementKindSectionHeader) {
        headReusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:HeaderIdentifier forIndexPath:indexPath];
        headReusableView.frame = CGRectZero;
        headReusableView.delegate = self;
        headReusableView.modeNumber = 1;
        [((ZLHeaderView *)headReusableView) setData:self.headerDict];
    }
    return headReusableView;
}

//执行的 headerView 代理  返回 headerView 的高度
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    CGFloat height = [self.topView getHeight];
    return CGSizeMake(320, height);
}

二、测试时所出现的坑:

2.1、 初始化控制器的时候要初始化布局

当出现下面的错误时,*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'
要从初始化控制器时看下是否初始化布局上找bug原因,即使是搭框架时的初始化添加子控制器时也不要忘记初始化布局

2.2、 当瀑布流需要headerView和footerView时候的小坑

用UIView来自定义headerView和footerView 千万不要忘了添加到CollectionView上 以及 瀑布流的上下间隔来显示headerView和footerView,否则会被遮盖住。

- (UIView *)headerView {
    if (!_headerView) {
        _headerView = [[UIView alloc] init];
        _headerView.height = headerViewH;
        _headerView.backgroundColor = YYPBackgroundColor;

        [self setupHeaderView]; //这里去根据需求自定义设置headerView相关布局
    }
    return _headerView;
}
    [collectView addSubview:self.headerView];    
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(YYPWaterflowLayout *)waterflowLayout {
    
    return UIEdgeInsetsMake(kNavBarHeight + headerViewH + 30, 20, 30, 20); // 这里是nav64,headerViewH就是headerView的高度
}

//返回HeaderView的大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    
    return CGSizeMake(kContentViewWidth, headerViewH);
}

运行瀑布流效果截图如下所示:

后期会不断的更新优化,进一步的完善。如果喜欢就点个喜欢吧…

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容