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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM