UICollectionView实现瀑布流

  • ViewController.h文件
@interface ViewController : UIViewController<UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UICollectionView *collectionView;

@end
  • ViewController.m文件
#import "ViewController.h"
#import "WaterFallFlowLayout.h"
#import "WaterFallUICollectionViewCell.h"
#define WIDTH ([UIScreen mainScreen].bounds.size.width-20)/3
CGFloat const kImageCount = 16;
static NSString *identifier = @"collectionView";
@interface ViewController ()
@property (nonatomic, strong) NSArray  *imageArr;
@end

@implementation ViewController

#pragma mark - 图片懒加载
-(NSArray*)imageArr{
    if (!_imageArr) {
        NSMutableArray *muArr = [NSMutableArray array];
        for (int i=1; i<=kImageCount; i++) {
            UIImage *image = [UIImage imageNamed:[[NSString alloc] initWithFormat:@"image%d",i]];
            [muArr addObject:image];
        }
        _imageArr = muArr;
    }
    return _imageArr;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    WaterFallFlowLayout *flowLayout = [[WaterFallFlowLayout alloc] init];
    self.collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];
    self.collectionView.backgroundColor = [UIColor redColor];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.collectionView registerClass:[WaterFallUICollectionViewCell class] forCellWithReuseIdentifier:identifier];
    [self.view addSubview:self.collectionView];
}

#pragma mark - UICollectionView dataSource
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return [self.imageArr count];
}

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    WaterFallUICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    cell.image = self.imageArr[indexPath.item];
    return cell;
}

#pragma mark - 计算图片高度
-(float)imageHeight:(float)height width:(float)width{
    /*
     高度/宽度 = 压缩后高度/压缩后宽度
     */
    float newHeight = height/width*(WIDTH);
    return newHeight;
}

#pragma mark - cell大小
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    UIImage *imge = [self.imageArr objectAtIndex:indexPath.row];
    float height = [self imageHeight:imge.size.height width:imge.size.width];
    return CGSizeMake(WIDTH, height);
}

#pragma mark - 边距
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    UIEdgeInsets edgeInsets = {5,5,5,5};
    return edgeInsets;
}

@end
  • WaterFallFlowLayout.h文件
#import <UIKit/UIKit.h>

@interface WaterFallFlowLayout : UICollectionViewFlowLayout
@property(nonatomic,assign)id<UICollectionViewDelegateFlowLayout> delegate;
@property(nonatomic,assign)NSInteger cellCount;//cell的个数
@property(nonatomic,strong)NSMutableArray *colArr;//存放列的高度
@property(nonatomic,strong)NSMutableDictionary *attributeDict;//cell的位置信息

@end
  • WaterFallFlowLayout.m文件
#import "WaterFallFlowLayout.h"
CGFloat const colCount = 3;
@implementation WaterFallFlowLayout
//准备布局:得到cell的总个数,为每个cell确定自己的位置
- (void)prepareLayout{
    [super prepareLayout];
    NSLog(@"prepareLayout");
    _colArr = [NSMutableArray array];
    _attributeDict = [NSMutableDictionary dictionary];
    self.delegate = (id<UICollectionViewDelegateFlowLayout>)self.collectionView.delegate;
    //获取cell的总个数
    _cellCount = [self.collectionView numberOfItemsInSection:0];
    if (_cellCount == 0) {
        return;
    }
    float top = 0;
    for (int i = 0; i < colCount; i++) {
        [_colArr addObject:[NSNumber numberWithFloat:top]];
    }
    //循环调用layoutForItemAtIndexPath方法,为每个cell布局,将indexPath传入,作为布局字典的key
    //layoutAttributesForItemAtIndexPath方法的实现,这里用到了一个布局字典,其实就是将每个cell的位置信息与indexPath相对应,将它们放到字典中,方便后面视图的检索
    for (int i = 0; i < _cellCount; i++) {
        [self layoutItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
    }

}

//此方法会多次调用,为每个cell布局
- (void)layoutItemAtIndexPath:(NSIndexPath *)indexPath{
    //通过协议得到cell的间隙
    NSLog(@"layoutItemAtIndexPath");
    UIEdgeInsets edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:indexPath.row];
    CGSize itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
    float col = 0;
    float shortHeight = [[_colArr objectAtIndex:col] floatValue];
    //找出高度最小的列,将cell加到最小列中
    for (int i = 0; i < _colArr.count; i++) {
        float height = [[_colArr objectAtIndex:i] floatValue];
        if (height < shortHeight) {
            shortHeight = height;
            col = i;
        }
    }
    float top = [[_colArr objectAtIndex:col] floatValue];
    NSLog(@"top:%f",top);
    //确定cell的frame
    CGRect frame = CGRectMake(edgeInsets.left + col * (edgeInsets.left + itemSize.width), top + edgeInsets.top, itemSize.width, itemSize.height);
    NSLog(@"frme:%f,%f,%f,%f",edgeInsets.left + col * (edgeInsets.left + itemSize.width),top + edgeInsets.top,itemSize.width,itemSize.height);
    //更新列高
    [_colArr replaceObjectAtIndex:col withObject:[NSNumber numberWithFloat:top + edgeInsets.top + itemSize.height]];

    //每个cell的frame对应一个indexPath,放入字典中
    [_attributeDict setObject:indexPath forKey:NSStringFromCGRect(frame)];
}

- (NSArray *)indexPathsOfItem:(CGRect)rect{
    //遍历布局字典通过CGRectIntersectsRect方法确定每个cell的rect与传入的rect是否有交集,如果结果为true,则此cell应该显示,将布局字典中对应的indexPath加入数组
    NSLog(@"indexPathsOfItem");
    NSMutableArray *array = [NSMutableArray array];
    for (NSString *rectStr in _attributeDict) {
        CGRect cellRect = CGRectFromString(rectStr);
        if (CGRectIntersectsRect(cellRect, rect)) {
            NSIndexPath *indexPath = _attributeDict[rectStr];
            [array addObject:indexPath];
        }
    }
    return array;
}

//返回cell的布局信息,如果忽略传入的rect一次性将所有的cell布局信息返回,图片过多时性能会很差
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSLog(@"layoutAttributesForElementsInRect");
    NSMutableArray *muArr = [NSMutableArray array];
    //indexPathsOfItem方法,根据传入的frame值计算当前应该显示的cell
    NSArray *indexPaths = [self indexPathsOfItem:rect];
    for (NSIndexPath *indexPath in indexPaths) {
      //  UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexPath];
        UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexPath];
        [muArr addObject:attribute];
    }
    return muArr;
}

//返回每个CollectionViewCell的属性
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
     for (NSString *rectStr in _attributeDict) {
         if (_attributeDict[rectStr]==indexPath) {
             attributes.frame = CGRectFromString(rectStr);

         }
     }
     return attributes;
}

//最后还要实现这个方法,返回collectionView内容的大小
//只需要遍历前面创建的存放列高的数组得到列最高的一个作为高度返回就可以了
- (CGSize)collectionViewContentSize{
    NSLog(@"collectionViewContentSize");
    CGSize size = self.collectionView.frame.size;
    float maxHeight = [[_colArr objectAtIndex:0] floatValue];
    //查找最高的列的高度
    for (int i = 0; i < _colArr.count; i++) {
        float colHeight = [[_colArr objectAtIndex:i] floatValue];
        if (colHeight > maxHeight) {
            maxHeight = colHeight;
        }
    }
    size.height = maxHeight;
    return size;
}
@end
  • WaterFallUICollectionViewCell.h文件
@interface WaterFallUICollectionViewCell : UICollectionViewCell
@property(nonatomic,strong) UIImage *image;
@end
  • WaterFallUICollectionViewCell.m文件
#import "WaterFallUICollectionViewCell.h"
#define WIDTH ([UIScreen mainScreen].bounds.size.width-20)/3

@implementation WaterFallUICollectionViewCell
- (void)setImage:(UIImage *)image{
    if (_image != image) {
        _image = image;
    }
    [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
    float newHeight = _image.size.height / _image.size.width * WIDTH;
    [_image drawInRect:CGRectMake(0, 0, WIDTH, newHeight)];
    self.backgroundColor = [UIColor grayColor];
}

@end
  • 最终效果如下图
    这里写图片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章