簡單的瀑布流實現(xiàn)。github地址

image
各種不規(guī)則的布局都可以利用UICollectionVIewLayout來實現(xiàn),因此我們要自定義一個layout來繼承系統(tǒng)的UICollectionViewLayout,所有工作都在這個類中進(jìn)行。
思路:
1、從上往下,哪一列最短,就把下一個item放在哪一列,因此我們需要定義一個字典來記錄每一列的最大y值
2、每一個item都有一個attributes,因此定義一個數(shù)組來保存每一個item的attributes

image
下面是具體步驟:
1.初始化需要用的總列數(shù)、列間距、行間距、距離collectionView的上下左右距離
//總列數(shù)
@property(nonatomic, assign) NSInteger columnCount;
//列間距
@property(nonatomic, assign) NSInteger columnSpacing;
//行間距
@property(nonatomic, assign) NSInteger rowSpacing;
//section到collectionView的邊距
@property (nonatomic, assign) UIEdgeInsets sectionInset;
//保存每一列最大y值的數(shù)組
@property(nonatomic, strong) NSMutableDictionary * maxYDic;
//保存每一個item的attributes的數(shù)組
@property(nonatomic, strong) NSMutableArray * attributesArray;
需要重寫的4個方法:
- (void)prepareLayout;
系統(tǒng)準(zhǔn)備在item進(jìn)行布局前會調(diào)用這個方法,我們重寫這個方法可以在方法里面預(yù)先設(shè)置好需要用到的變量屬性等。
- (CGSize)collectionViewContentSize;
由于collectionView將item的布局任務(wù)委托給了layout對象,那么滾動區(qū)域的大小對于它而言是不可知的。自定義布局必須在這個方法里面計算出顯示內(nèi)容的大小,包括supplementaryView和decorationView在內(nèi)
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
我們可以在這個方法中創(chuàng)建并且返回特別定制的布局屬性
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect;
核心方法。collectionView調(diào)用這個方法并將自身坐標(biāo)中的矩形傳過來,這個巨型代表著當(dāng)前collectionView的可視范圍,我們需要在這個方法中返回一個包括UICollectionViewLayoutAttributes對象的數(shù)組,這個布局屬性對象決定了當(dāng)前顯示的item的大小、層次、可視屬性在內(nèi)的布局屬性
- (void)prepareLayout
{
[super prepareLayout];
//初始化字典。有幾列就有幾個鍵值對 value為列的y值
for (int i = 0; i < self.columnCount; i ++) {
self.maxYDic[@(i)] = @(self.sectionInset.top);
}
//根據(jù)collectionView獲取總共有多少個item
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
[self.attributesArray removeAllObjects];
//為每一個item創(chuàng)建一個attributes并存入數(shù)組
for (int i = 0; i < itemCount; i ++) {
UICollectionViewLayoutAttributes * attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attributesArray addObject:attributes];
}
}
//遍歷字典,找出最長的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([self.maxYDic[maxIndex] floatValue] < [obj floatValue]) {
maxIndex = key;
}
}];
//用來設(shè)置每個item的attributes
CGFloat itemWidth = (collectionViewWidth - self.sectionInset.left - self.sectionInset.right - (self.columnCount - 1) * self.columnSpacing) / self.columnCount;
CGFloat itemHeight = 0;
//通過代理或者block將圖片的寬度傳出去,然后根據(jù)比例獲得圖片的高度
if (self.itemHeightBlock){
itemHeight = self.itemHeightBlock(itemWidth,indexPath);
}else{
if ([self.delegate respondsToSelector:@selector(waterfallLayout:itemHeightForWidth:atIndexPath:)]) {
itemHeight = [self.delegate waterfallLayout:self itemHeightForWidth:itemWidth atIndexPath:indexPath];
}
}
CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;
//item的y值 = 最短列的最大y值+ 行間距
CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;
BUG:在重寫UICollectionViewCell的時候,如果通過代碼創(chuàng)建,然后在initWithFrame里面添加一個imageView,那么我怎么設(shè)置frame都有問題,但是通過xib創(chuàng)建則是沒有問題的,如果有朋友知道希望告知。
具體實現(xiàn)方法還可以參考這篇文章