平时生活和工作中的iOSweb开发iOS开发

瀑布流小框架

2015-11-18  本文已影响4013人  Tuberose


#import "ViewController.h"
#import "CYWaterFlowLayout.h"

@interface ViewController ()<UICollectionViewDataSource>

@end

@implementation ViewController


static NSString * const CYShopId = @"shop";

- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建布局
    CYWaterFlowLayout *layout = [[CYWaterFlowLayout alloc] init];

    // 创建CollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.dataSource = self;
    [self.view addSubview:collectionView];

    // 注册
    [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:CYShopId];
}


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

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYShopId forIndexPath:indexPath];

    cell.backgroundColor = [UIColor orangeColor];

    NSInteger tag = 10;
    UILabel *label = (UILabel *)[cell.contentView viewWithTag:tag];
    if (label == nil) {
        label = [[UILabel alloc] init];
        label.tag = tag;
        [cell.contentView addSubview:label];
    }

    label.text = [NSString stringWithFormat:@"%zd", indexPath.item];
    [label sizeToFit];

    return cell;
}

#import "CYWaterFlowLayout.h"


@interface CYWaterFlowLayout()

@end

@implementation CYWaterFlowLayout


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

/**
 * 初始化
 */
- (void)prepareLayout
{
    [super prepareLayout];
    
    // 开始创建每一个cell对应的布局属性
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger i = 0; i < count; i++) {
        // 创建位置
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        // 创建布局属性
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        // 设置布局属性的frame
        attrs.frame = CGRectMake(arc4random(300), arc4random(300), arc4random(300), arc4random(300));
        [self.attrsArray addObject:attrs];
    }
}

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

#import "CYWaterFlowLayout.h"


@interface CYWaterFlowLayout()

@end

@implementation CYWaterFlowLayout


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

/**
 * 初始化
 */
- (void)prepareLayout
{
    [super prepareLayout];
    // 清除之前所有的布局属性
    [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];

    // 设置布局属性的frame
    attrs.frame = CGRectMake(arc4random(300), arc4random(300), arc4random(300), arc4random(300));

    return attrs;
}


/** 默认的列数 */
static const NSInteger CYDefaultColumnCount = 3;
/** 每一列之间的间距 */
static const CGFloat CYDefaultColumnMargin = 10;
/** 每一行之间的间距 */
static const CGFloat CYDefaultRowMargin = 10;
/** 边缘间距 */
static const UIEdgeInsets CYDefaultEdgeInsets = {10, 10, 10, 10};
static const UIEdgeInsets CYDefaultEdgeInsets = (10, 10, 10, 10);
+  而UIEdgeInsets是一个结构体,我们如果按平时像这么写,这就是一个函数调用了,虽然它只是类名函数。你如果调函数,意味着你在程序运行过程中才会执行,而像我们这种静态变量是�编译器确定的,也就是说编译器编译的时候才会确认这个固定的值,这样就不能执行这个代码了。函数是运行时调用,这样就不行
+  这样的话我们用土方法用一个{}解决,因为它是结构体,相当于把10,10,10,10分别赋值给四个属性,所以今后要写静态变量的结构体,这也是一种方法
    // collectionView的宽度
    CGFloat collectionViewW = self.collectionView.frame.size.width;

    // 设置布局属性的frame
    CGFloat w = (collectionViewW - CYDefaultEdgeInsets.left - CYDefaultEdgeInsets.right - (CYDefaultColumnCount - 1) * CYDefaultColumnMargin) / CYDefaultColumnCount;
    CGFloat h = 50 + arc4random_uniform(100);
/** 存放所有列的当前高度 */
@property (nonatomic, strong) NSMutableArray *columnHeights;
@end

@implementation CYWaterFlowLayout

- (NSMutableArray *)columnHeights
{
    if (!_columnHeights) {
        _columnHeights = [NSMutableArray array];
    }
    return _columnHeights;
}
        //     找出高度最短的那一列
        __block NSInteger destColumn = 0;
        //  目标那一列
        __block CGFloat minColumnHeight = MAXFLOAT;
        //  先保证minColumnHeight是最大的才能保证第一个家伙比他小
        //  block要改外面的变量,要加一个修饰符__block
        [self.columnHeights enumerateObjectsUsingBlock:^(NSNumber *columnHeightNumber, NSUInteger idx, BOOL *stop) {
            CGFloat columnHeight = columnHeightNumber.doubleValue;
            if (minColumnHeight > columnHeight) {
                minColumnHeight = columnHeight;
                destColumn = idx;
                //  目标列
            }
        }];
```objc
// 找出高度最短的那一列
NSInteger destColumn = 0;
CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
 //  第0个值我已经取出来了,我们就可以从第1个开始和0进行比较了
 //  这里虽然是小小的优化少了一次遍历,但是不能小瞧少这一次的遍历,因为今后这个方法调得很频繁
 //  这也是这里用for循环而不用block的原因,我们能控制从哪儿开始遍历
for (NSInteger i = 1; i < CYDefaultColumnCount; i++) {
    // 取得第i列的高度
    CGFloat columnHeight = [self.columnHeights[i] doubleValue];

    if (minColumnHeight > columnHeight) {
        minColumnHeight = columnHeight;
        destColumn = i;
    }
}
-  我们已经找到了最短那列的高度了,所以X和Y值就可以确定了

```objc
    CGFloat x = CYDefaultEdgeInsets.left + destColumn * (w + CYDefaultColumnMargin);
    CGFloat y = minColumnHeight + CYDefaultRowMargin;
    attrs.frame = CGRectMake(x, y, w, h);
    // 更新最短那列的高度
    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
[super prepareLayout];

    // 清除以前计算的所有高度
    [self.columnHeights removeAllObjects];
    for (NSInteger i = 0; i < CYDefaultColumnCount; i++) {
        [self.columnHeights addObject:@(CYDefaultEdgeInsets.top)];
    }

    CGFloat x = CYDefaultEdgeInsets.left + destColumn * (w + CYDefaultColumnMargin);
    CGFloat y = minColumnHeight;
    if (y != CYDefaultEdgeInsets.top) {
        y += CYDefaultRowMargin;
    }
    attrs.frame = CGRectMake(x, y, w, h);

    // 更新最短那列的高度
    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
- (CGSize)collectionViewContentSize
{
    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < CYDefaultColumnCount; i++) {
        // 取得第i列的高度
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

        if (maxColumnHeight < columnHeight) {
            maxColumnHeight = columnHeight;
        }
    }
    return CGSizeMake(0, maxColumnHeight + CYDefaultEdgeInsets.bottom);
}

#import "CYWaterFlowLayout.h"

/** 默认的列数 */
static const NSInteger CYDefaultColumnCount = 3;
/** 每一列之间的间距 */
static const CGFloat CYDefaultColumnMargin = 10;
/** 每一行之间的间距 */
static const CGFloat CYDefaultRowMargin = 10;
/** 边缘间距 */
static const UIEdgeInsets CYDefaultEdgeInsets = {10, 10, 10, 10};

@interface CYWaterFlowLayout()
/** 存放所有cell的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
/** 存放所有列的当前高度 */
@property (nonatomic, strong) NSMutableArray *columnHeights;
@end

@implementation CYWaterFlowLayout

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

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

/**
 * 初始化
 */
- (void)prepareLayout
{
    [super prepareLayout];

    // 清除以前计算的所有高度
    [self.columnHeights removeAllObjects];
    for (NSInteger i = 0; i < CYDefaultColumnCount; i++) {
        [self.columnHeights addObject:@(CYDefaultEdgeInsets.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 - CYDefaultEdgeInsets.left - CYDefaultEdgeInsets.right - (CYDefaultColumnCount - 1) * CYDefaultColumnMargin) / CYDefaultColumnCount;
    CGFloat h = 50 + arc4random_uniform(100);

    // 找出高度最短的那一列
    NSInteger destColumn = 0;
    CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < CYDefaultColumnCount; i++) {
        // 取得第i列的高度
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

        if (minColumnHeight > columnHeight) {
            minColumnHeight = columnHeight;
            destColumn = i;
        }
    }

    CGFloat x = CYDefaultEdgeInsets.left + destColumn * (w + CYDefaultColumnMargin);
    CGFloat y = minColumnHeight;
    if (y != CYDefaultEdgeInsets.top) {
        y += CYDefaultRowMargin;
    }
    attrs.frame = CGRectMake(x, y, w, h);

    // 更新最短那列的高度
    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));

    return attrs;
}

- (CGSize)collectionViewContentSize
{
    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < CYDefaultColumnCount; i++) {
        // 取得第i列的高度
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

        if (maxColumnHeight < columnHeight) {
            maxColumnHeight = columnHeight;
        }
    }
    return CGSizeMake(0, maxColumnHeight + CYDefaultEdgeInsets.bottom);
}

@end


#import <UIKit/UIKit.h>

@interface CYShop : NSObject
@property (nonatomic, assign) CGFloat w;
@property (nonatomic, assign) CGFloat h;
@property (nonatomic, copy) NSString *img;
@property (nonatomic, copy) NSString *price;
@end
#import "CYShop.h"

@implementation CYShop

@end
#import <UIKit/UIKit.h>
@class CYShop;
@interface CYShopCell : UICollectionViewCell
@property (nonatomic, strong) CYShop *shop;
@end
#import "CYShopCell.h"
#import "CYShop.h"
#import "UIImageView+WebCache.h"

@interface CYShopCell()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *priceLabel;
@end

@implementation CYShopCell

- (void)setShop:(CYShop *)shop
{
    _shop = shop;

    // 1.图片
    [self.imageView sd_setImageWithURL:[NSURL URLWithString:shop.img] placeholderImage:[UIImage imageNamed:@"loading"]];

    // 2.价格
    self.priceLabel.text = shop.price;
}
@end


/** 所有的商品数据 */
@property (nonatomic, strong) NSMutableArray *shops;
@property (nonatomic, weak) UICollectionView *collectionView;
#import "ViewController.h"
#import "CYWaterFlowLayout.h"
#import "CYShop.h"
#import "MJExtension.h"
#import "MJRefresh.h"
#import "CYShopCell.h"

@interface ViewController () <UICollectionViewDataSource>
/** 所有的商品数据 */
@property (nonatomic, strong) NSMutableArray *shops;

@property (nonatomic, weak) UICollectionView *collectionView;
@end

@implementation ViewController

- (NSMutableArray *)shops
{
    if (!_shops) {
        _shops = [NSMutableArray array];
    }
    return _shops;
}

static NSString * const CYShopId = @"shop";

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupLayout];

    [self setupRefresh];
}

- (void)setupRefresh
{
    //  下拉刷新
    self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewShops)];
    //  一进入就刷新数据
    [self.collectionView.mj_header beginRefreshing];
    //  上拉自动刷新
    self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreShops)];
    //  结束刷新
    self.collectionView.mj_footer.hidden = YES;
}

//  下拉刷新数据
- (void)loadNewShops
{
    //  模拟网络请求延时加载
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //  字典转模型,从plist文件中加载商品数据模型数组
        NSArray *shops = [CYShop mj_objectArrayWithFilename:@"1.plist"];
        //  每次刷新都要重新加载数据,所以先移除,再重新加载数据
        [self.shops removeAllObjects];
        [self.shops addObjectsFromArray:shops];

        // 刷新数据
        [self.collectionView reloadData];
        //  数据回来后结束刷新
        [self.collectionView.mj_footer endRefreshing];
    });
}

//  上拉刷新数据
- (void)loadMoreShops
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *shops = [CYShop mj_objectArrayWithFilename:@"1.plist"];
        [self.shops addObjectsFromArray:shops];

        // 刷新数据
        [self.collectionView reloadData];

        [self.collectionView.mj_footer endRefreshing];
    });
}

- (void)setupLayout
{
    // 创建布局
    CYWaterFlowLayout *layout = [[CYWaterFlowLayout alloc] init];

    // 创建CollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.backgroundColor = [UIColor whiteColor];
    collectionView.dataSource = self;
    [self.view addSubview:collectionView];

    // 注册(从Xib加载)
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYShopCell class]) bundle:nil] forCellWithReuseIdentifier:CYShopId];

    self.collectionView = collectionView;
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    self.collectionView.mj_footer.hidden = self.shops.count == 0;
    return self.shops.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CYShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYShopId forIndexPath:indexPath];

    cell.shop = self.shops[indexPath.item];

    return cell;
}
  
@end

    +  代理是弱引用,用weak,加一个id,协议以自己的名字开头
    ```objc
@interface CYWaterFlowLayout : UICollectionViewLayout
/** 代理 */
@property (nonatomic, weak) id<CYWaterFlowLayoutDelegate> delegate;
+  这个方法里不再用optional了,optional用的时候那些方法是可有可无的,假如你监听我内部实现,可以监听也可以不监听。所以这里我用 required,得强制实现,因为你不告诉我高度,我没法显示,现在的级别不一样了,你作为代理,你必须告诉我,不告诉我,我就不帮你显示了,我直接报错。
+  而且这个代理方法必须要有个返回值,,返回每个cell对应的高度
+  这里的代理方法也是参考官方tableView代理去写的         
```objc

@required

    +  让控制器成为我的代理
     ```objc
      @interface ViewController () <UICollectionViewDataSource, CYWaterFlowLayoutDelegate>

    layout.delegate = self;

#import <UIKit/UIKit.h>

@class CYWaterFlowLayout;

@protocol CYWaterFlowLayoutDelegate <NSObject>
@required
- (CGFloat)waterflowLayout:(CYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;
@end

@interface CYWaterFlowLayout : UICollectionViewLayout
/** 代理 */
@property (nonatomic, weak) id<CYWaterFlowLayoutDelegate> delegate;
@end
#pragma mark - <CYWaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(CYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth
{
    CYShop *shop = self.shops[index];

    return itemWidth * shop.h / shop.w;
}
    // 设置布局属性的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];

@optional
- (CGFloat)columnCountInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout;
- (CGFloat)columnMarginInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout;
- (CGFloat)rowMarginInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout;
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout;
// 声明一下,点语法就能调用了
- (CGFloat)rowMargin;
- (CGFloat)columnMargin;
- (NSInteger)columnCount;
- (UIEdgeInsets)edgeInsets;
@end

@implementation CYWaterFlowLayout

#pragma mark - 常见数据处理
- (CGFloat)rowMargin
{
    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
        return [self.delegate rowMarginInWaterflowLayout:self];
    } else {
        return CYDefaultRowMargin;
    }
}

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

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

- (UIEdgeInsets)edgeInsets
{
    if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {
        return [self.delegate edgeInsetsInWaterflowLayout:self];
    } else {
        return CYDefaultEdgeInsets;
    }
}
#pragma mark - <CYWaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(CYWaterflowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth
{
    CYShop *shop = self.shops[index];

    return itemWidth * shop.h / shop.w;
}

- (CGFloat)rowMarginInWaterflowLayout:(CYWaterflowLayout *)waterflowLayout
{
    return 20;
}

- (CGFloat)columnCountInWaterflowLayout:(CYWaterflowLayout *)waterflowLayout
{
    if (self.shops.count <= 50) return 2;
    return 3;
}

- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(CYWaterflowLayout *)waterflowLayout
{
    return UIEdgeInsetsMake(10, 20, 30, 100);
}



- (CGFloat)rowMarginInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{ 
      return 20;
}
- (CGFloat)columnCountInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{
     if (self.shops.count <= 50) return 2; 
     return 3;
     //  当现实内容少于50个的时候为两列,大于50个的时候为3列
}
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{
     return UIEdgeInsetsMake(10, 20, 30, 100);
}






#import <UIKit/UIKit.h>

@class CYWaterFlowLayout;

@protocol CYWaterFlowLayoutDelegate <NSObject>
@required
- (CGFloat)waterflowLayout:(CYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;

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

@interface CYWaterFlowLayout : UICollectionViewLayout
/** 代理 */
@property (nonatomic, weak) id<CYWaterFlowLayoutDelegate> delegate;
@end
#import "CYWaterFlowLayout.h"

/** 默认的列数 */
static const NSInteger CYDefaultColumnCount = 3;
/** 每一列之间的间距 */
static const CGFloat CYDefaultColumnMargin = 10;
/** 每一行之间的间距 */
static const CGFloat CYDefaultRowMargin = 10;
/** 边缘间距 */
static const UIEdgeInsets CYDefaultEdgeInsets = {10, 10, 10, 10};

@interface CYWaterFlowLayout()
/** 存放所有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 CYWaterFlowLayout

#pragma mark - 常见数据处理
- (CGFloat)rowMargin
{
    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
        return [self.delegate rowMarginInWaterflowLayout:self];
    } else {
        return CYDefaultRowMargin;
    }
}

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

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

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

#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
#import "ViewController.h"
#import "CYWaterFlowLayout.h"
#import "CYShop.h"
#import "MJExtension.h"
#import "MJRefresh.h"
#import "CYShopCell.h"

@interface ViewController () <UICollectionViewDataSource, CYWaterFlowLayoutDelegate>
/** 所有的商品数据 */
@property (nonatomic, strong) NSMutableArray *shops;

@property (nonatomic, weak) UICollectionView *collectionView;
@end

@implementation ViewController

- (NSMutableArray *)shops
{
    if (!_shops) {
        _shops = [NSMutableArray array];
    }
    return _shops;
}

static NSString * const CYShopId = @"shop";

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupLayout];

    [self setupRefresh];
}

- (void)setupRefresh
{
    self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewShops)];
    [self.collectionView.mj_header beginRefreshing];

    self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreShops)];
    self.collectionView.mj_footer.hidden = YES;
}

- (void)loadNewShops
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *shops = [CYShop mj_objectArrayWithFilename:@"1.plist"];
        [self.shops removeAllObjects];
        [self.shops addObjectsFromArray:shops];

        // 刷新数据
        [self.collectionView reloadData];

        [self.collectionView.mj_footer endRefreshing];
    });
}

- (void)loadMoreShops
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *shops = [CYShop mj_objectArrayWithFilename:@"1.plist"];
        [self.shops addObjectsFromArray:shops];

        // 刷新数据
        [self.collectionView reloadData];

        [self.collectionView.mj_footer endRefreshing];
    });
}

- (void)setupLayout
{
    // 创建布局
    CYWaterFlowLayout *layout = [[CYWaterFlowLayout alloc] init];
    layout.delegate = self;

    // 创建CollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.backgroundColor = [UIColor whiteColor];
    collectionView.dataSource = self;
    [self.view addSubview:collectionView];

    // 注册
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYShopCell class]) bundle:nil] forCellWithReuseIdentifier:CYShopId];

    self.collectionView = collectionView;
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    self.collectionView.mj_footer.hidden = self.shops.count == 0;
    return self.shops.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CYShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYShopId forIndexPath:indexPath];

    cell.shop = self.shops[indexPath.item];

    return cell;
}

#pragma mark - <CYWaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(CYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth
{
    CYShop *shop = self.shops[index];

    return itemWidth * shop.h / shop.w;
}

- (CGFloat)rowMarginInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{
    return 20;
}

- (CGFloat)columnCountInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{
    if (self.shops.count <= 50) return 2;
    return 3;
}

- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(CYWaterFlowLayout *)waterflowLayout
{
    return UIEdgeInsetsMake(10, 10, 10, 10);
}

@end
上一篇 下一篇

猜你喜欢

热点阅读