UICollectionView之瀑布流

什么是瀑布流?

瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格。

实现瀑布流的必要条件:等宽不等高,宽高比!

代码如下:

控制器.m文件

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //实例化布局(自定义布局)
    WaterFallLayout * waterLayout = [[WaterFallLayout alloc] init];
    
    waterLayout.delegate = self;

    //创建collectionView
    UICollectionView * collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:waterLayout];
    collectionView.backgroundColor = [UIColor whiteColor];
    //注册waterFallCell
    [collectionView registerClass:[WaterFallCell class] forCellWithReuseIdentifier:ID];
    
    self.collectionView = collectionView;
    
    collectionView.dataSource = self;
    
    [self.view addSubview:collectionView];

}

#pragma mark - UICollectionViewDataSource方法

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{ 
    //返回数据的个数
    return self.dataArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    WaterFallCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
    
    //设置背景色
    cell.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(255.0) / 255.0) green:(arc4random_uniform(255.0) / 255.0) blue:(arc4random_uniform(255.0) / 255.0) alpha:1.0];
    return cell;
}
</pre><pre name="code" class="objc">//自定义布局的代理方法
- (CGFloat)fallLayout:(WaterFallLayout *)fallLayout heightForWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath
{
    WaterModel * model = self.dataArray[indexPath.item];
    
    return model.h / model.w * width;
}

自定义布局的.h文件

#import <UIKit/UIKit.h>
@class WaterFallLayout;
@protocol WaterFallLayoutDelegate <NSObject>
//代理方法
- (CGFloat)fallLayout:(WaterFallLayout *)fallLayout heightForWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath;
@end

@interface WaterFallLayout : UICollectionViewFlowLayout
/** 列数 */
@property (nonatomic,assign) NSInteger colCount;
<pre name="code" class="objc">/** 内边距 */
@property (assign,nonatomic) UIEdgeInsets sectionInsets;/** 列间距 */@property (nonatomic,assign) CGFloat colMargin;/** 行间距 */@property (nonatomic,assign) CGFloat rowMargin;@property (weak,nonatomic) id<WaterFallLayoutDelegate> delegate;@end

 
自定义布局的.m文件 

#import "WaterFallLayout.h"

@interface WaterFallLayout ()
/** 用来记录每列最小的y值 */
@property (strong,nonatomic) NSMutableArray * array;
/** 存放所有的布局属性 */
@property (strong,nonatomic) NSMutableArray * attrsArray;
@end
@implementation WaterFallLayout

- (NSMutableArray *)array
{
    if (_array == nil) {
        
        _array = [NSMutableArray array];
    }
    
    return _array;
}

- (NSMutableArray *)attrsArray
{
    if (_attrsArray == nil) {
        
        _attrsArray = [NSMutableArray array];
    }
    
    return _attrsArray;
}
- (instancetype)init
{
    if (self = [super init]) {
        
        self.sectionInsets = UIEdgeInsetsMake(10, 10, 10, 10);
        self.rowMargin = 10;
        self.colMargin = 10;
        self.colCount = 3;
    }
    
    return self;
}
/**
 *  返回每一个cell的布局属性
 *
 *  @param indexPath cell的下标
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    //cell的宽度
    CGFloat cellWidth = (self.collectionView.frame.size.width - self.sectionInsets.left * 2 - (self.colCount - 1) * self.rowMargin) / self.colCount;
    
    //cell的高度
    CGFloat cellheight = [self.delegate fallLayout:self heightForWidth:cellWidth indexPath:indexPath];
    
    //假设取数组中第0个元素y值最小
    CGFloat minY = [self.array[0] doubleValue];
    
    //最小列
    NSInteger minColumn = 0;
    
    for(NSInteger i = 0;i < self.colCount;i ++ ){
        
        CGFloat currentY = [self.array[i] doubleValue];
        
        if (minY > currentY) {
            
            minY = currentY;
            minColumn = i;
        }
    }
    
     //cell的x
    
    CGFloat cellX = self.sectionInsets.left + (cellWidth + self.colMargin) * minColumn;
     //cell的y
    
    CGFloat cellY = minY + self.rowMargin;
    
    UICollectionViewLayoutAttributes * attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    attrs.frame = CGRectMake(cellX, cellY, cellWidth, cellheight);
    
    //更新最小y值
    self.array[minColumn] = @(CGRectGetMaxY(attrs.frame));
    
    NSLog(@"%@",NSStringFromCGRect(attrs.frame));
    return attrs;
}

- (void)prepareLayout
{
    [super prepareLayout];
    
    for (NSInteger i = 0; i < self.colCount; i ++) {
        
        self.array[i] = @"0";
    }
   
    [self.attrsArray removeAllObjects];
    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger i = 0; i < cellCount; i ++) {
        
        NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        
        UICollectionViewLayoutAttributes * att = [self layoutAttributesForItemAtIndexPath:indexPath];
        
        [self.attrsArray addObject:att];
    }

}

//用来确定垂直滚动的范围
- (CGSize)collectionViewContentSize
{
    if (!self.array.count) {
        
        return CGSizeZero;
    }
    
    CGFloat maxY = [self.array[0] doubleValue];
    
    for (NSInteger i = 0 ; i < self.colCount; i ++) {
        
        CGFloat currentY = [self.array[i] doubleValue];
        
        if (maxY < currentY) {
            
            maxY = currentY;
        }
    }
    
    return CGSizeMake(0, maxY + self.sectionInsets.bottom);
}
/**
 *  返会范围内的cell布局属性
 *
 *  @param rect 范围
 */
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{

    return self.attrsArray;
    
}
//更新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}
 
@end




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值