• 自定义UICollectionViewLayout 实现瀑布流


    今天研究了一下自定义UICollectionViewLayout。 看了看官方文档,要自定义UICollectionViewLayout,需要创建一个UICollectionViewLayout的子类。同时,可以通过一下3个方法传递布局信息、contentSize、cells的信息等。

    一、继承UICollectionViewLayout,重写以下方法

    1.通过prepareLayout方法来计算预先计算需要提供的布局信息。
    2.通过collectionViewContentSize方法来返回contentSize
    3.通过layoutAttributesForElementsInRect: 方法来返回每个cell的信息

    二、创建UICollectionViewLayoutAttributes,创建的方法有一下三种

    1.layoutAttributesForCellWithIndexPath:
    2.layoutAttributesForSupplementaryViewOfKind:withIndexPath:
    3.layoutAttributesForDecorationViewOfKind:withIndexPath:

    其中,layoutAttributesForCellWithIndexPath:方法创建cell的属性,layoutAttributesForSupplementaryViewOfKind:withIndexPath:创建补充视图的属性,如header、footer,layoutAttributesForDecorationViewOfKind:withIndexPath:创建修饰视图的属性

    基础知识介绍完了,接下讲具体示例 创建一个UICollectionViewLayout的子类WKFlowLayout,

    @interface WKFlowLayout : UICollectionViewLayout
    
    @property (nonatomic, strong) NSMutableDictionary *layoutInformation;
    @property (nonatomic) NSInteger maxNumCols;
    
    @end
    
    static NSUInteger CellWidth = 100;  
    static CGFloat ContentHeight;
    
    @implementation WKFlowLayout
    {
        NSMutableArray *_yOffsets;//存储各列的当前offest
    }

    接下来在prepareLayout预先计算布局信息

    - (void)prepareLayout
    {
        _maxNumCols = 2;//设置为两列
    
        _yOffsets = [NSMutableArray arrayWithCapacity:_maxNumCols];
        for (int i = 0; i < _maxNumCols; i++) {
            [_yOffsets addObject:@0];
        }
    
        //初始化cell的宽度
        CellWidth = self.collectionView.bounds.size.width / _maxNumCols;
    
        //事先创建好UICollectionViewLayoutAttributes
        _layoutInformation = [NSMutableDictionary dictionary];
    
        NSIndexPath *indexPath;
        NSInteger numSections = [self.collectionView numberOfSections];
        for(NSInteger section = 0; section < numSections; section++){
            NSInteger numItems = [self.collectionView numberOfItemsInSection:section];
            for(NSInteger item = 0; item < numItems; item++){
                indexPath = [NSIndexPath indexPathForItem:item inSection:section];
                UICollectionViewLayoutAttributes *attributes =
                [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
                NSInteger col = indexPath.item % _maxNumCols;
    
                WKFlowLayoutDataSource *ds = self.collectionView.dataSource;
    
                NSNumber *height = ds.dataSource[indexPath.row];
                attributes.frame = CGRectMake(col * CellWidth, [_yOffsets[col] floatValue], CellWidth, [height floatValue]);
                CGFloat yOffset;
    
                yOffset = [_yOffsets[col] floatValue] + [height floatValue];
                NSLog(@"yOffset:%f col:%ld", yOffset, (long)col);
    
                _yOffsets[col] = @(yOffset);
    
                [_layoutInformation setObject:attributes forKey:indexPath];
                //计算滚动高度
                ContentHeight = MAX(ContentHeight, CGRectGetMaxY(attributes.frame));
            }
        }
    }

    剩下的代码

    - (CGSize)collectionViewContentSize
    {
        CGFloat contentWidth = self.collectionView.bounds.size.width;
    
        CGSize contentSize = CGSizeMake(contentWidth, ContentHeight);
        return contentSize;
    }
    
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        NSMutableArray *myAttributes = [NSMutableArray arrayWithCapacity:self.layoutInformation.count];
        for(NSString *key in self.layoutInformation.allKeys){
            UICollectionViewLayoutAttributes *attributes = [self.layoutInformation objectForKey:key];
    
                if(CGRectIntersectsRect(rect, attributes.frame)){
                    [myAttributes addObject:attributes];
    
            }
        }
        return myAttributes;
    
    }

    以上就是主要的实现代码了,需要注意的是,在prepareLayout中预先算出所有的布局信息适用于cell个数小于1000,超过之后在耗时就过长了,用户体验不好,同时需要在- (BOOL)shouldInvalidateLayoutForBoundsChange:方法中返回NO,此方法表示不需要再滚动过程中不停的调用prepareLayout

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

    示例代码 再附上官方的circleLayout

  • 相关阅读:
    自动轮播
    哈夫曼树的应用-金条划分
    计算两个日期相差的天数
    数据结构之算术表达式
    动态规划-矩阵最短路径
    动态规划-换钱最少货币数
    字母数字密码破解
    荷兰国旗问题
    集合并集
    进制数位幸运数
  • 原文地址:https://www.cnblogs.com/pretty-guy/p/5132707.html
Copyright © 2020-2023  润新知