随机放大的不规则瀑布流

栏目: IOS · 发布时间: 6年前

内容简介:利用UICollectionView实现一个随机放大的不规则瀑布流,图片宽高比和内容使用服务器接口返回的数据,效果如下图:接口:数据:

利用UICollectionView实现一个随机放大的不规则瀑布流,图片宽高比和内容使用服务器接口返回的数据,效果如下图:

随机放大的不规则瀑布流

接口:

https://www.easy-mock.com/mock/5cff89e36c54457798010709/shop/finderlist
复制代码

数据:

{
  "data": [
    {
      "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561019906083&di=2bbf7db2124067fe80739cce43a2b00e&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201410%2F05%2F20141005095943_QY5e8.jpeg",
      "width": "1200",
      "height": "2249"
    },
    {
      "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561020045178&di=56eb95088ce1a23bbd16776ebcedb837&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fc8107b13c3bfd1fa8835f5dc80c541b64c6b9e901a8f7-RLJBJP_fw658",
      "width": "658",
      "height": "872"
    },
    ...
    {
      "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561020635692&di=cd0dedd961380917af46c536e7f6600b&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201707%2F29%2F20170729215622_tTLBP.thumb.700_0.jpeg",
      "width": "700",
      "height": "701"
    }
  ]
}
复制代码

每个图片数据都指定了图片的宽和高,由于需要放大,而被放大占用两列宽度的图片顶部必须要对齐,所以需要将高度差别不大的两列高度做矫正,这里采用的规则是。

  • 插入图片的高度距离左右某一张的高度差值小于的它高度20%就将图片高度强行对齐高度。
  • 每一排不允许出现连续两个放大的图片。
  • 每一列不允许出现连续连个放大的图片。

具体实现

1,重写的方法

创建一个UICollectionViewLayout的子类

@protocol JKRFallsLayoutDelegate <NSObject>

@optional
/// 列数
- (CGFloat)columnCountInFallsLayout:(JKRFallsLayout *)fallsLayout;
/// 列间距
- (CGFloat)columnMarginInFallsLayout:(JKRFallsLayout *)fallsLayout;
/// 行间距
- (CGFloat)rowMarginInFallsLayout:(JKRFallsLayout *)fallsLayout;
/// collectionView边距
- (UIEdgeInsets)edgeInsetsInFallsLayout:(JKRFallsLayout *)fallsLayout;
/// 返回图片模型
- (JKRImageModel *)modelWithIndexPath:(NSIndexPath *)indexPath;

@end

@interface JKRFallsLayout : UICollectionViewLayout

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

@end
复制代码

重写以下方法:

// collectionView 首次布局和之后重新布局的时候会调用
// 并不是每次滑动都调用,只有在数据源变化的时候才调用
- (void)prepareLayout {
    // 重写必须调用super方法
    [super prepareLayout];
}

// 返回布局属性,一个UICollectionViewLayoutAttributes对象数组
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return [super layoutAttributesForElementsInRect:rect];
}

// 计算布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    return [super layoutAttributesForItemAtIndexPath:indexPath];
}

// 返回collectionView的ContentSize
- (CGSize)collectionViewContentSize {
    return [super collectionViewContentSize];
}
复制代码

2,布局计算

要实现布局的计算,需要创建以下几个属性

@property (nonatomic, strong) NSMutableArray<UICollectionViewLayoutAttributes *> *attrsArray; ///< 所有的cell的布局
@property (nonatomic, strong) NSMutableArray *columnHeights;                                  ///< 每一列的高度
@property (nonatomic, assign) NSInteger noneDoubleTime;                                       ///< 没有生成大尺寸次数
@property (nonatomic, assign) NSInteger lastDoubleIndex;                                      ///< 最后一次大尺寸的列数

- (CGFloat)columnCount;     ///< 列数
- (CGFloat)columnMargin;    ///< 列边距
- (CGFloat)rowMargin;       ///< 行边距
- (UIEdgeInsets)edgeInsets; ///< collectionView边距
复制代码

在- (void)prepareLayout方法中遍历需要计算的cell,调用计算布局的方法,并将获取的布局属性保存到attrsArray数组中:

- (void)prepareLayout {
    // 重写必须调用super方法
    [super prepareLayout];
    
    if ([self.collectionView numberOfItemsInSection:0] == PageCount && self.attrsArray.count > PageCount) {
        [self.attrsArray removeAllObjects];
        [self.columnHeights removeAllObjects];
    }
    // 当列高度数组为空时,即为第一行计算,每一列的基础高度加上collection的边框的top值
    if (!self.columnHeights.count) {
        for (NSInteger i = 0; i < self.columnCount; i++) {
            [self.columnHeights addObject:@(self.edgeInsets.top)];
        }
    }
    // 遍历所有的cell,计算所有cell的布局
    for (NSInteger i = self.attrsArray.count; i < [self.collectionView numberOfItemsInSection:0]; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
        // 计算布局属性并将结果添加到布局属性数组中
        [self.attrsArray addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }
}

// 返回布局属性,一个UICollectionViewLayoutAttributes对象数组
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return self.attrsArray;
}

// 返回collectionView的ContentSize
- (CGSize)collectionViewContentSize {
    // collectionView的contentSize的高度等于所有列高度中最大的值
    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
    for (NSInteger i = 1; i < self.columnCount; i++) {
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];
        if (maxColumnHeight < columnHeight) {
            maxColumnHeight = columnHeight;
        }
    }
    return CGSizeMake(0, maxColumnHeight + self.edgeInsets.bottom);
}
复制代码

columnHeights保存保存每一列的总高度,cell的宽度和高度,通过随机数+不重复放大+矫正的原则来计算,下面有代码详细注释:

// 计算布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    // cell的宽度
    CGFloat w = (self.collectionView.frame.size.width - self.edgeInsets.left - self.edgeInsets.right - self.columnMargin * (self.columnCount - 1)) / self.columnCount;
    // cell的高度
    JKRImageModel*shop = [self.delegate modelWithIndexPath:indexPath];
    CGFloat h = shop.height / shop.width * w;
    
    // cell应该拼接的列数
    NSInteger destColumn = 0;
    // 高度最小的列数高度
    CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
    // 获取高度最小的列数
    for (NSInteger i = 1; i < self.columnCount; i++) {
        CGFloat columnHeight = [self.columnHeights[i] doubleValue];
        if (minColumnHeight > columnHeight) {
            minColumnHeight = columnHeight;
            destColumn = i;
        }
    }
    
    // 计算cell的x
    CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
    // 计算cell的y
    CGFloat y = minColumnHeight;
    if (y != self.edgeInsets.top) {
        y += self.rowMargin;
    }
    
    // 判断是否放大
    if (destColumn < self.columnCount - 1                               // 放大的列数不能是最后一列(最后一列方法超出屏幕)
        && _noneDoubleTime >= 1                                         // 如果前个cell有放大就不放大,防止连续出现两个放大
        && arc4random() % 100 > 33                                      // 33%几率不放大
        && [self.columnHeights[destColumn] doubleValue] == [self.columnHeights[destColumn + 1] doubleValue] // 当前列的顶部和下一列的顶部要对齐
        && (_lastDoubleIndex != destColumn)                             // 最后一次放大的列不等当前列,防止出现连续两列出现放大不美观
        ) {
        _noneDoubleTime = 0;
        _lastDoubleIndex = destColumn;
        // 重定义当前cell的布局:宽度*2,高度*2
        attrs.frame = CGRectMake(x, y, w * 2 + self.columnMargin, h * 2 + self.rowMargin);
        self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
        self.columnHeights[destColumn + 1] = @(CGRectGetMaxY(attrs.frame));
    } else {
        // 正常cell的布局
        if (self.columnHeights.count > destColumn + 1 && ABS(y + h - [self.columnHeights[destColumn + 1] doubleValue]) < h * 0.2) {
            // 当前cell填充后和上一列的高度偏差不超过cell最大高度的10%,就和下一列对齐
            attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn + 1] doubleValue] - y);
        } else if (destColumn >= 1 && ABS(y + h - [self.columnHeights[destColumn - 1] doubleValue]) < h * 0.2) {
            // 当前cell填充后和上上列的高度偏差不超过cell最大高度的10%,就和下一列对齐
            attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn - 1] doubleValue] - y);
        } else {
            attrs.frame = CGRectMake(x, y, w, h);
        }
        // 当前cell列的高度就是当前cell的最大Y值
        self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
        _noneDoubleTime += 1;
    }
    // 返回计算获取的布局
    return attrs;
}
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms Unlocked

Algorithms Unlocked

Thomas H. Cormen / The MIT Press / 2013-3-1 / USD 25.00

Have you ever wondered how your GPS can find the fastest way to your destination, selecting one route from seemingly countless possibilities in mere seconds? How your credit card account number is pro......一起来看看 《Algorithms Unlocked》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具