Irregular random amplification cascade

UICollectionView implemented using a random irregular cascade amplification, picture aspect ratio, and the content usage data server interface returned results as shown:

interface:

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

data:

{
  "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"
    }
  ]
}
复制代码

Each picture data specifies the width and height of the image, due to the need to enlarge, are amplified occupied width of the top two images must be aligned, it needs to be little difference in height of two height correction made.

  • Inserted a distance of about one picture height is the height difference is less than 20% of the height of it will be forced to the heights of the picture height.
  • Each row does not allow two consecutive larger image appears.
  • Each column in a row even a larger image is not allowed.
  • Use the network interface data
  • Waterfall pictures using custom size
  • Refresh the drop-down support
  • Support from several custom columns
  • Support for custom picture spacing
  • Support from the custom border from

Implementation

1, a method override

Create a subclass of 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
复制代码

Rewrite the following methods:

// 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, layout calculation

To achieve calculate the layout, you need to create the following attributes

@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边距
复制代码

In - (void) prepareLayout traversal methods to be calculated Cell, layout calculation method call, and to store the retrieved attributes to attrsArray array layout:

- (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 Save to save the overall height of each column, the width and height of the Cell, by a random number will not be repeated enlarged + + calculated correction principle, the code in detail below Notes:

// 计算布局属性
- (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;
}
复制代码

3, source code

Click to view the source code .

Reproduced in: https: //juejin.im/post/5d0b57816fb9a07ead5a0164

Guess you like

Origin blog.csdn.net/weixin_34184158/article/details/93182193