自定義UICollectionViewLayout 實現瀑布流

jopen 8年前發布 | 20K 次閱讀 iOS開發 移動開發

今天研究了一下自定義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

來自: http://www.cnblogs.com/pretty-guy/p/5132707.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!