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
  • 最終效果如下圖
    這裏寫圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章