自定义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

 

posted @ 2016-01-15 11:06  pretty guy  阅读(2162)  评论(0编辑  收藏  举报