天生不是作曲家collectionview相关iOS && Android

自定义瀑布流布局

2016-03-15  本文已影响224人  论丶道

(本文主要讲一下现在比较流行的一种布局方式----瀑布流布局 如有写的不好的地方 还请多多指正 感谢)

1、功能分析

如图所示: 我们可以看到该布局中的每个元素有一个共同的特点就是等宽不等高. 而且当一行排列完成之后在下一行进行排列时都是从最短的那一列开始排,否则的话就会让每一列的差距越来越大从而显得非常不美观.

2、实现思路
3、code

以上进行简单分析之后就要开始动手了 既然是自定义布局 我们就需要创建一个继承自UICollectionViewLayout的类来实现布局 在这个类中我们需要实现以下几个方法

-(void)prepareLayout
{
    [super prepareLayout];
    //清除之前计算的所有高度
    [self.colunmHeights removeAllObjects];
    for (NSInteger i = 0; i < ZDDefaultColumnCount; i++) {
        [self.colunmHeights addObject:@(ZDDefaultEdgeInsets.top)];
    }
    //清除之前所有的布局
    [self.attrsArray removeAllObjects];
    //创建每一个cell对应的布局属性
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger i = 0; i < count; i++) {
        //创建位置
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        //获取indexPath位置cell对应的布局属性
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attrsArray addObject:attrs];
    }
}

其中self.colunmHeightsself.attrsArray是自己定义的两个属性 分别用来保存所有列的当前高度以及所有cell的布局属性(两个都是可变数组类型)

-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    //创建布局属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    //collectionView的宽度
    CGFloat collectionViewW = self.collectionView.frame.size.width;
    //设置布局属性的frame
    CGFloat w = (collectionViewW - ZDDefaultEdgeInsets.left - ZDDefaultEdgeInsets.right - (ZDDefaultColumnCount - 1) * ZDDefaultColumnMargin) / ZDDefaultColumnCount;
    CGFloat h = 50 + arc4random_uniform(100);
    
    //找出高度最短的那一列
    NSInteger destColumn = 0;
    CGFloat minColumnHeight = [self.colunmHeights[0] doubleValue];
    for (NSInteger i = 1; i < ZDDefaultColumnCount; i++) {
        CGFloat columnHeight = [self.colunmHeights[i] doubleValue];
        
        if (minColumnHeight > columnHeight) {
            minColumnHeight = columnHeight;
            destColumn = i;
        }
    }
    CGFloat x = ZDDefaultEdgeInsets.left + destColumn * (w + ZDDefaultColumnMargin);
    CGFloat y = minColumnHeight;
    if (y != ZDDefaultEdgeInsets.top) {
        y += ZDDefaultRowMargin;
    }
    attrs.frame = CGRectMake(x, y, w, h);
    
    //更新最短那列的高度
    self.colunmHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
    
    // 记录内容的高度
    CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
    if (self.contentHeight < columnHeight) {
        self.contentHeight = columnHeight;
    }
    return attrs;
}

其中self.contentHeight是自定义的一个属性 用来保存内容的高度

-(CGSize)collectionViewContentSize
{
    return CGSizeMake(0, self.contentHeight + ZDDefaultEdgeInsets.bottom);
}
4、优化
@class ZDWaterfallLayout;
@protocol ZDWaterfallLayoutDelegate <NSObject>
@required
-(CGFloat)waterfallLayout:(ZDWaterfallLayout *)waterfallLayout heightForItemAtIndex:(NSInteger)index itemWidth:(CGFloat)itemWidth;
@optional
//列数
-(CGFloat)columnCountInWaterfallLayout:(ZDWaterfallLayout *)waterfallLayout;
//每一列之间的间距
-(CGFloat)columnMarginInWaterfallLayout:(ZDWaterfallLayout *)waterfallLayout;
//每一行之间的间距
-(CGFloat)rowMarginInWaterfallLayout:(ZDWaterfallLayout *)waterfallLayout;
//cell的边距
-(UIEdgeInsets)edgeInsetsInWaterfallLayout:(ZDWaterfallLayout *)waterfallLayout;
@end
@interface ZDWaterfallLayout : UICollectionViewLayout
/**布局代理属性*/
@property (nonatomic,weak) id<ZDWaterfallLayoutDelegate>delegate ;
@end
-(CGFloat)rowMargin
{
    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterfallLayout:)]) {
        return [self.delegate rowMarginInWaterfallLayout:self];
    }else{
        return ZDDefaultRowMargin;
    }
}
-(CGFloat)columnMargin
{
    if ([self.delegate respondsToSelector:@selector(columnMarginInWaterfallLayout:)]) {
        return [self.delegate columnMarginInWaterfallLayout:self];
    }else{
        return ZDDefaultColumnMargin;
    }
}
-(NSInteger)columnCount
{
    if ([self.delegate respondsToSelector:@selector(columnCountInWaterfallLayout:)]) {
        return [self.delegate columnCountInWaterfallLayout:self];
    }else{
        return ZDDefaultColumnCount;
    }
}
-(UIEdgeInsets)edgeInsets
{
    if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterfallLayout:)]) {
        return [self.delegate edgeInsetsInWaterfallLayout:self];
    }else{
        return ZDDefaultEdgeInsets;
    }
}

ZDDefaultRowMargin ZDDefaultColumnMargin ZDDefaultColumnCount ZDDefaultEdgeInsets这是我自定义的默认值
接下来想要改变布局效果就很简单了 只需要通过具体实现几个代理方法就可以搞定 比如我想排5列 只需要实现如下方法即可:

-(CGFloat)columnCountInWaterfallLayout:(ZDWaterfallLayout *)waterfallLayout
{
    return 5;
}

显示效果如下:


就是这么轻松愉快~
具体demo请看我的github
上一篇 下一篇

猜你喜欢

热点阅读