自定义瀑布流

DKFlowLayout类

DKFlowLayout.h

// 在.h里@class, 在.m里#import
@class DKFlowLayout;

@protocol DKFlowLayoutDelegate <NSObject>

- (CGFloat) layout: (DKFlowLayout *)layout heightForItemAtIndexPath:(NSIndexPath *) indexPath width: (CGFloat) width;

@end

@interface DKFlowLayout : UICollectionViewLayout

// 指定有多少列(一般2到4列)
@property (nonatomic, assign) NSInteger columnCounts;
// 设置距离屏幕四周的边界距离
@property (nonatomic, assign) UIEdgeInsets edgeInsets;
// 列间距
@property (nonatomic, assign) NSInteger columnSpace;
// 行间距
@property (nonatomic, assign) NSInteger rowSpace;

@property (nonatomic, assign) id<DKFlowLayoutDelegate> delegate;

@end

DKFlowLayout.m

#define kWidth self.collectionView.frame.size.width

@interface DKFlowLayout ()
// 用来保存每一列的 Y 值
@property (nonatomic, retain) NSMutableDictionary *columnDic;
// 用来存放 item 的 attribute 对象
@property (nonatomic, retain) NSMutableArray *attributsArray;

@end

@implementation DKFlowLayout


// 1.初始化方法
- (instancetype) init {
    if (self = [super init]) {
        self.columnDic = [NSMutableDictionary dictionary];
        self.attributsArray = [NSMutableArray array];
    }
    return self;
}

// 2.prepareLayout方法: collectionView 布局 item 的时候,该方法会被执行
- (void) prepareLayout {
    [super prepareLayout];

    // 根据列数进行遍历, 给起始的每列都加上上边界的距离
    for (NSInteger i = 0; i < self.columnCounts; i++) {
        NSString *key = [NSString stringWithFormat:@"%ld", i];
        self.columnDic[key] = @(self.edgeInsets.top);
    }

    // 获取 collectionView 上 item 的个数
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    // 循环遍历每个 item
    for (int i = 0; i < count; i++) {
        [self setItemFrame:i];
    }
}
// 3.自定义一个方法设置每个 item 的信息
- (void) setItemFrame: (NSInteger) index {
    // 1.首先获取当前最短的那一列
    // 2.设置每个 item 的 frame 的值

    // 初始化一个字符串,用它来记录当前最短的列的下标
    __block NSString *minColumn = @"0";
    // 遍历字典的每个 key 和 value
    [self.columnDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        // 如果遍历得到的值小于默认列的值,就将 key 的值赋值到 minColumn 里
        if ([obj floatValue] < [self.columnDic[minColumn] floatValue]) {
            minColumn = key;
        }
    }];

    // 宽 = (屏幕宽 - 左边界 - 右边界 - 列间距 * (列数 - 1)) / 列数
    CGFloat width = (kWidth - self.edgeInsets.left - self.edgeInsets.right - self.columnSpace * (self.columnCounts - 1)) / self.columnCounts;
    // x = 左边距 + (宽 + 列间距) * 列的下标(对应当前最短那列的下标)
    CGFloat x = self.edgeInsets.left + (width + self.columnSpace) * [minColumn floatValue];
    // 创建一个 indexPath
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
    // 高
    CGFloat height = [self.delegate layout:self heightForItemAtIndexPath:indexPath width:width];
    // Y
    // 先找到当前最短那列的 Y 值
    CGFloat minY = [self.columnDic[minColumn] floatValue];
    // 为最短的列更新 Y 轴的高度 = minY + 高 + 行间距
    self.columnDic[minColumn] = @(minY + height + self.rowSpace);

    // UICollectionViewLayoutAttributes: 设置 item 的 frame, bounds, 透明度等属性
    UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attribute.frame = CGRectMake(x, minY, width, height);
    [self.attributsArray addObject:attribute];
}

// 4.把所有样式返回,告诉系统如何去布局
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return self.attributsArray;
}

// 5.设置滚动的范围
- (CGSize) collectionViewContentSize {
    // 滚动范围以最长的列作为依据
    __block NSString *maxY = @"0";
    [self.columnDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([obj floatValue] > [self.columnDic[maxY] floatValue]) {
            maxY = key;
        }
    }];
    CGFloat h = [self.columnDic[maxY] floatValue] + self.edgeInsets.bottom;
    return CGSizeMake(0, h);
}

@end

MyCell类

MyCell.h

@interface MyCell : UICollectionViewCell
@property (nonatomic, retain) UIImageView *picImageView;
@property (nonatomic, retain) Picture *pic;
@end

MyCell.m

@implementation MyCell

- (instancetype) initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.picImageView = [[UIImageView alloc] init];
        [self.contentView addSubview:self.picImageView];
    }
    return self;
}

- (void) layoutSubviews {
    [super layoutSubviews];
    // 为了避免重用之后造成子视图上的尺寸有问题, 所以在 layoutSubviews 方法重新设置子视图的尺寸
    self.picImageView.frame = self.contentView.frame;
}

// 重写 cell 的 set 方法
- (void) setPic:(Picture *)pic {
    if (_pic != pic) {
        [_pic release];
        _pic = [pic retain];
    }
    [self.picImageView sd_setImageWithURL:[NSURL URLWithString:_pic.thumbURL]];
}

@end

Picture类

.h

@interface Picture : NSObject
@property (nonatomic, copy) NSString *thumbURL;
@property (nonatomic, copy) NSNumber *width;
@property (nonatomic, copy) NSNumber *height;
@end

eg.

- (void) createData {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    NSArray *tempArr = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    self.arr = [NSMutableArray array];
    for (NSDictionary *dic in tempArr) {
        Picture *pic = [[Picture alloc] init];
        [pic setValuesForKeysWithDictionary:dic];
        [self.arr addObject:pic];
    }
}

- (void) createView {
    DKFlowLayout *flowLayout = [[DKFlowLayout alloc] init];
    flowLayout.columnCounts = 3;
    flowLayout.columnSpace = 20;
    flowLayout.rowSpace = 15;
    flowLayout.edgeInsets = UIEdgeInsetsMake(10, 10, 10, 10);
    flowLayout.delegate = self;

    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:flowLayout];
    self.collectionView.dataSource = self;
    self.collectionView.delegate = self;
    [self.view addSubview: self.collectionView];
    [self.collectionView registerClass:[MyCell class] forCellWithReuseIdentifier:NSStringFromClass([MyCell class])];
    self.collectionView.backgroundColor = [UIColor whiteColor];
}

- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.arr.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([MyCell class]) forIndexPath:indexPath];
    Picture *pic = self.arr[indexPath.row];
//    [cell.picImageView sd_setImageWithURL:[NSURL URLWithString:pic.thumbURL]];
    cell.pic = pic;

    return cell;
}

- (CGFloat) layout:(DKFlowLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath width:(CGFloat)width {
    Picture *pic = self.arr[indexPath.row];
    CGFloat height = pic.height.floatValue * width / pic.width.floatValue;
    return height;
}

// Data.json
[
  {
  "thumbURL": "http://c.hiphotos.baidu.com/baike/w%3D268/sign=1396d7c49922720e7bcee5fc43ca0a3a/b3119313b07eca807f594b41902397dda044ad345982ab0b.jpg",
  "width": 111.6,
  "height": 180
  },
  ...
  ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值