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