使用UICollectionView 重写UICollectionViewLayout实现瀑布流

来源:互联网 发布:生死狙击球棒数据变异 编辑:程序博客网 时间:2024/05/10 23:13

实现瀑布流的原理是什么呢 有几个 比较重要的方法  是必须要实现的:

1、 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 当collectionView视图位置有新改变(发生移动)时调用,其若返回YES则重新布局

2、 - (void)prepareLayout 准备好布局时调用。此时collectionView所有属性都已确定。我们在这里可以将collectionView当做画布,有了画布后,我们便可以在其上面画出每个item

3、 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 返回collectionView视图中所有视图的属性(UICollectionViewLayoutAttributes)数组

4、 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 返回indexPath对应item的属性

5、- (CGSize)collectionViewContentSize 设置collectionView的可显示范围

 其中最主要的方法是3和4俩种方法 ,在3方法中返回所有视图属性数组,并根据这些属性进行布局,而4方法则返回每个item的属性,我们则在这里设置每个item的属性(主要是frame),就可以让collectionView按照我们的意愿进行布局了!(在此我们一半不需要用到1方法,若item属性根据滑动改变,此时就需要随时进行布局改变)


首先  我们在.h文件中 我们需要写一些东西 (列数、行间距、内边距、列间距)

.h

@property(nonatomic, assign)UIEdgeInsets sectionInset; //sectionInset
@property(nonatomic, assign)CGFloat lineSpacing;  //line space
@property(nonatomic, assign)CGFloat itemSpacing; //item space

@property(nonatomic, assign)CGFloat colCount; //column count

//实现重写flowLayout让代理去完成

@property(nonatomic, weak)id<WaterfallFlowLayoutDelegate> delegate;


//并且 在此处 我们必须要有一个  计算高度的方法,同时这个方法在.m中必须要实现的(这里我使用代理实现,当然也可以使用block)

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterfallFlowLayout *)collectionViewLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;

//当然如果我们需要添加header和footer 的时候  同样可以声明他的代理方法 例如:

//section header
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(WaterfallFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
//section footer
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(WaterfallFlowLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

接下来  我们在.m中实现方法


- (void)prepareLayout
{
    [super prepareLayout];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

- (CGSize)collectionViewContentSize
{
    __block NSString * maxCol = @"0";
    
    //遍历找出最高的列
    [self.colunMaxYDic enumerateKeysAndObjectsUsingBlock:^(NSString * column, NSNumber *maxY, BOOL *stop) {
        
        if ([maxY floatValue] > [self.colunMaxYDic[maxCol] floatValue]) {
            
            maxCol = column;
        }
        
    }];
    
    return CGSizeMake(0, [self.colunMaxYDic[maxCol] floatValue]);
}

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
{
    
    __block NSString * minCol = @"0";
    
    //遍历找出最短的列
    [self.colunMaxYDic enumerateKeysAndObjectsUsingBlock:^(NSString * column, NSNumber *maxY, BOOL *stop) {
        
        if ([maxY floatValue] < [self.colunMaxYDic[minCol] floatValue]) {
            
            minCol = column;
        }
    }];
    
    //    宽度
    CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right- (self.colCount-1) * self.itemSpacing)/self.colCount;
    
    //    高度
    CGFloat height = 0;
    
    if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForWidth:atIndexPath:)]) {
        
        //
        height = [self.delegate collectionView:self.collectionView layout:self heightForWidth:width atIndexPath:indexPath];
    }
    
    CGFloat x = self.sectionInset.left + (width + self.itemSpacing) * [minCol intValue];
    
    CGFloat space = 0.0;
    
    if (indexPath.item < self.colCount) {
        
        space = 0.0;
        
    }else{
        
        space = self.lineSpacing;
    }
    
    CGFloat y =[self.colunMaxYDic[minCol] floatValue] + space;
    
    //    跟新对应列的高度
    self.colunMaxYDic[minCol] = @(y + height);
    
    //    计算位置
    UICollectionViewLayoutAttributes * attri = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    attri.frame = CGRectMake(x, y, width, height);
    
    return attri;
}

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{
    
    __block NSString * maxCol = @"0";
    
    //遍历找出最高的列
    [self.colunMaxYDic enumerateKeysAndObjectsUsingBlock:^(NSString * column, NSNumber *maxY, BOOL *stop) {
        
        if ([maxY floatValue] > [self.colunMaxYDic[maxCol] floatValue]) {
            
            maxCol = column;
        }
    }];
    
    //header
    if ([UICollectionElementKindSectionHeader isEqualToString:elementKind]) {
        
        UICollectionViewLayoutAttributes *attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
        
        //size
        CGSize size = CGSizeZero;
        
        if ([self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
            
            size = [self.delegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:indexPath.section];
            
        }
        
        CGFloat x = self.sectionInset.left;
        
        CGFloat y = [[self.colunMaxYDic objectForKey:maxCol] floatValue] + self.sectionInset.top;
        
        //    跟新所有对应列的高度
        for(NSString *key in self.colunMaxYDic.allKeys)
        {
            self.colunMaxYDic[key] = @(y + size.height);
        }
        
        attri.frame = CGRectMake(x , y, size.width, size.height);
        
        return attri;
    }
    
    //footer
    else{
        UICollectionViewLayoutAttributes *attri = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];
        
        //size
        CGSize size = CGSizeZero;
        
        if ([self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
            
            size = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:indexPath.section];
        }
        
        CGFloat x = self.sectionInset.left;
        
        CGFloat y = [[self.colunMaxYDic objectForKey:maxCol] floatValue];
        
        //    跟新所有对应列的高度
        for(NSString *key in self.colunMaxYDic.allKeys)
        {
            self.colunMaxYDic[key] = @(y + size.height + self.sectionInset.bottom);
        }
        
        attri.frame = CGRectMake(x , y, size.width, size.height);
        
        return attri;
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    for(NSInteger i = 0;i < self.colCount; i++)
    {
        NSString * col = [NSString stringWithFormat:@"%ld",(long)i];
        
        self.colunMaxYDic[col] = @0;
    }
    
    NSMutableArray * attrsArray = [NSMutableArray array];
    
    NSInteger section = [self.collectionView numberOfSections];
    
    for (NSInteger i = 0 ; i < section; i++) {
        
        //获取header的UICollectionViewLayoutAttributes
        UICollectionViewLayoutAttributes *headerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]];
        
        [attrsArray addObject:headerAttrs];
        
        //获取item的UICollectionViewLayoutAttributes
        NSInteger count = [self.collectionView numberOfItemsInSection:i];
        for (NSInteger j = 0; j < count; j++) {
            
            UICollectionViewLayoutAttributes * attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];
            
            [attrsArray addObject:attrs];
        }
        
        //获取footer的UICollectionViewLayoutAttributes
        UICollectionViewLayoutAttributes *footerAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]];
        
        [attrsArray addObject:footerAttrs];
    }
    
    return  attrsArray;
}

到此所有的重写layout 部分就全部完成

接下来  我们只需要在ViewController里面使用即可

ViewController.m中

@interface ViewController ()<WaterfallFlowLayoutDelegate, UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong)UICollectionView *collectionView;

@property (nonatomic, strong)WaterfallFlowLayout *layout;

@end
首先  我们 初始化collectionView 此处必须使用重写后的FlowLayout

self.layout = [WaterfallFlowLayout alloc]init];

self.collectionView = [[UICollectionView alloc]initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:self.layout];

最后加载到视图上面

[self.view addSubview:self.collectionView];

UICollectionView必须要写的俩个方法,同时,此处有一个我们计算每个item大小的代理方法

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterfallFlowLayout*)collectionViewLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath
{

    //此处我是是用了一个model类来存储数据的model里面存储了图片等信息,同时 我在model类里面声明了item的宽度和高度

    DataModel * model = self.viewModel.dataArray[indexPath.section][indexPath.item];
    return model.h / model.w * width;
}

然后  我们在

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

}

此方法里面 我们可以加载每个item 的数据

到此处 一个 较为完整的 瀑布流就布局完成了,如果有什么问题的话  还希望  和每个大神交流探讨,希望大家能过得  开心!!!

1 0
原创粉丝点击