瀑布流

瀑布流

字数994  阅读1291  评论7 


瀑布流.png

效果:


瀑布流.gif

实现方案

  1. 利用UICollectionView,重写布局,实现很多购物app上面常见的瀑布流效果,由于UICollectionView自带循环利用效果,性能可靠实现方便。
  2. 利用UIScrollView,布局实现思路大体一样,需要自己手动写循环利用功能,较为复杂与麻烦。
  3. 使用并排的多个UITableView,移除自身滚动效果,让容纳多个UITableView的容器View滚动,实现瀑布流效果,但是扩展性差,不推荐使用。

实现过程

说明:利用广义MVC的思想,抽出了纯粹的自定义布局,不依赖任何模型数据,超轻量级瀑布流实现方案,以后只需讲代码中ZXRWaterFallLayout.hZXRWaterFallLayout.m拖入项目,实现相应的代理与数据源方法,即可实现瀑布流效果。

一、自定义布局实现
-------- .h的实现
// 1.设置代理,使得外界使用代理方面控制内部显示细节
@property (nonatomic, weak) id<ZXRWaterFallLayoutDelegate> delegate;

// 2.构造方法,使得外界通过重写代理方面,控制细节
@protocol ZXRWaterFallLayoutDelegate <NSObject>

@required  //必须实现
// 返回每个item的宽高
- (CGFloat)waterflowLayout:(ZXRWaterFallLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;

@optional  //可选实现
// 返回列数
- (CGFloat)columnCountInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout;
// 返回列间距
- (CGFloat)columnMarginInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout;
// 返回行间距
- (CGFloat)rowMarginInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout;
// 返回边缘间距
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout;

-------- .m的实现
// 提供和重写get方法,实现对代理方法的实时监控
- (CGFloat)rowMargin;
- (CGFloat)columnMargin;
- (NSInteger)columnCount;
- (UIEdgeInsets)edgeInsets;

- (CGFloat)rowMargin
{
    if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
        return [self.delegate rowMarginInWaterflowLayout:self];
    } else {
        return ZXRDefaultRowMargin; // 默认间距
    }
}
。。。

#pragma mark - 初始化
- (void)prepareLayout
{
    [super prepareLayout];

    self.contentHeight = 0;

    // 清除以前计算的所有高度
    [self.columnHeights removeAllObjects];
    for (NSInteger i = 0; i < self.columnCount; i++) {
        [self.columnHeights addObject:@(self.edgeInsets.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];
    }
}
#pragma mark - 决定cell的排布
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

核心代码
#pragma mark - 返回indexPath位置cell对应的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 创建布局属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    // collectionView的宽度
    CGFloat collectionViewW = self.collectionView.frame.size.width;

    // 设置布局属性的frame
    CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;
    CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];

    // 找出高度最短的那一列
    NSInteger destColumn = 0;
    CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < self.columnCount; i++) {
        // 取得第i列的高度
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];

        if (minColumnHeight > columnHeight) {
            minColumnHeight = columnHeight;
            destColumn = i;
        }
    }

    CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
    CGFloat y = minColumnHeight;
    if (y != self.edgeInsets.top) {
        y += self.rowMargin;
    }
    attrs.frame = CGRectMake(x, y, w, h);

    // 更新最短那列的高度
    self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));

    // 记录内容的高度
    CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
    if (self.contentHeight < columnHeight) {
        self.contentHeight = columnHeight;
    }
    return attrs;
}
二、外界使用布局方式
// 1.创建布局、设置代理
ZXRWaterFallLayout *layout = [[ZXRWaterFallLayout alloc] init];
layout.delegate = self;
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];

// 2.实现代理方法
// 必须实现
- (CGFloat)waterflowLayout:(ZXRWaterFallLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth
{
    ZXRShop *shop = self.shops[index];

    return itemWidth * shop.h / shop.w;
}

// 可选实现
- (CGFloat)rowMarginInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout
{
    return 10;
}
- (CGFloat)columnCountInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout
{
    return 3;
}
- (UIEdgeInsets)edgeInsetsInWaterflowLayout:(ZXRWaterFallLayout *)waterflowLayout
{
    return UIEdgeInsetsMake(10, 10, 10, 10);
}
总结

受到苹果自身控件UITableView的启发,利用代理,或者数据源提供方法给外部实现,从而实现了自定义布局控件与数据的解耦,可以作为超轻量级框架带到其他项目中,感觉还是不错的。
功能虽然不强大,MVC思想值得回味。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值