UICollectionView的布局主要是通過UICollectionViewLayout的子類來管理的,所以我們可以重寫UICollectionViewLayout來實(shí)現(xiàn)對(duì)UICollectionView布局樣式的修改。
實(shí)現(xiàn)原理
其實(shí)實(shí)現(xiàn)的本質(zhì)就是重新計(jì)算UICollectionVIew每個(gè)item的frame和UICollectionVIew的contentSize。
-
核心代碼
// 必須實(shí)現(xiàn)的父類方法 - (void)prepareLayout { [super prepareLayout]; if (self.itemsCount != [self.collectionView numberOfItemsInSection:0]) { self.itemsCount = [self.collectionView numberOfItemsInSection:0]; self.itemWidth = (ScreenWidth - (self.interval * (self.columnNumbers + 1))) / self.columnNumbers; } } // 設(shè)置UICollectionView的內(nèi)容大小,道理與UIScrollView的contentSize類似 - (CGSize)collectionViewContentSize { CGFloat contentHeight = 0; for (NSNumber *number in self.columnsHeight) { contentHeight = MAX(contentHeight, number.floatValue); }; return CGSizeMake(ScreenWidth, contentHeight); } // 初始話Layout外觀,返回所有的布局屬性 // 這個(gè)方法在滑動(dòng)的時(shí)候會(huì)調(diào)用 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { // TODO:// 這兒可以做緩存來提高效率 // 每次清空事前的item的height,重新計(jì)算 [self.columnsHeight removeAllObjects]; for (int i = 0; i < _columnNumbers; i ++) { [self.columnsHeight addObject:[NSNumber numberWithFloat:0]]; } NSMutableArray *attributes = [[NSMutableArray alloc] init]; for (int i = 0; i < self.itemsCount; i ++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]]; } return attributes; } // 根據(jù)不同的indexPath,給出布局 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat itemHeight = [self.delegate collectionView:self.collectionView layout:self heightForItemAtIndexPath:indexPath]; // 篩選UI上顯示的最短的那一列 __block CGFloat minHeight = [self.columnsHeight firstObject].floatValue; __block NSUInteger minIndex = 0; [self.columnsHeight enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { minHeight = MIN(minHeight, obj.floatValue); if (minHeight == obj.floatValue) { minIndex = idx; } }]; UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];; CGFloat x = minIndex * (self.interval + self.itemWidth) + self.interval; CGFloat y = minHeight + self.interval; layoutAttributes.frame = CGRectMake(x, y, self.itemWidth, itemHeight); self.columnsHeight[minIndex] = [NSNumber numberWithFloat:self.columnsHeight[minIndex].floatValue + itemHeight + self.interval]; return layoutAttributes; } -
涉及到的屬性和代理(.h文件)
@protocol ZMCollectionViewFlowLayoutDelegate; @interface ZMCollectionViewFlowLayout : UICollectionViewLayout /** 瀑布流顯示的列數(shù),默認(rèn)顯示兩列 */ @property (nonatomic, assign) NSUInteger columnNumbers; /** 每列之間的間隔距離,默認(rèn)10 */ @property (nonatomic, assign) CGFloat interval; @property (nonatomic, weak) id<ZMCollectionViewFlowLayoutDelegate> delegate; @end @protocol ZMCollectionViewFlowLayoutDelegate <NSObject> // 獲取item的高度,應(yīng)為默認(rèn)item的寬度可以通過間隔和列數(shù)計(jì)算出來的,所以這兒只需要高度即可 - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath; @end -
.m文件
@interface ZMCollectionViewFlowLayout()@property (nonatomic, assign) NSUInteger itemsCount; @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, strong) NSMutableArray <NSNumber *> *columnsHeight; // 保存當(dāng)前每一列的高度 @end -
使用
1,像UICollectionViewLayout一樣使用就行- (UICollectionView *)collecionView { if (!_collecionView) { ZMCollectionViewFlowLayout *layout = [[ZMCollectionViewFlowLayout alloc] init]; layout.delegate = self; layout.interval = 1; layout.columnNumbers = 3; // 顯示的列數(shù) _collecionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; _collecionView.dataSource = self; _collecionView.delegate = self; } return _collecionView; }
2,實(shí)現(xiàn)其代理
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.dataList[indexPath.row].floatValue; // 返回每個(gè)item的高度
}
- 結(jié)果

Paste_Image.png