collectionView能實現各中吊炸天的佈局,其精髓就在於UICollectionViewLayout,所以咱們要自定義一個layout來繼承系統的UICollectionViewLayout,全部工做都在這個類中進行。數組
1.定義所需屬性佈局
瀑布流的思路就是,從上往下,那一列最短,就把下一個item放在哪一列,所以咱們須要定義一個字典來記錄每一列的最大y值atom
每個item都有一個attributes,所以定義一個數組來保存每個item的attributes。spa
咱們還必須知道有多少列以及列間距、行間距、section到collectionView的邊距。代理
//總列數
@property (nonatomic, assign) NSInteger columnCount;
//列間距
@property (nonatomic, assign) NSInteger columnSpacing;
//行間距
@property (nonatomic, assign) NSInteger rowSpacing;
//section到collectionView的邊距
@property (nonatomic, assign) UIEdgeInsets sectionInset;
//保存每一列最大y值的數組
@property (nonatomic, strong) NSMutableDictionary *maxYDic;
//保存每個item的attributes的數組
@property (nonatomic, strong) NSMutableArray *attributesArray;
2.重寫系統方法code
咱們一共須要重寫4個方法對象
1)- (void)prepareLayout
2)- (CGSize)collectionViewContentSize
3)- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
4)- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
- (void)prepareLayout 方法繼承
佈局前的一些準備工做都在這裏進行,初始化字典,有幾列就有幾個鍵值對,key爲第幾列,value爲列的最大y值,初始值爲上內邊距:圖片
for
(int i = 0; i < self.columnCount; i++) {
self.maxYDic[@(i)] = @(self.sectionInset.top);
}
建立每一個item的attributes,並存入數組:ci
//根據collectionView獲取總共有多少個item
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
//爲每個item建立一個attributes並存入數組
for
(int i = 0; i < itemCount; i++) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[self.attributesArray addObject:attributes];
}
- (CGSize)collectionViewContentSize 方法
用來計算collectionView的contentSize
通常瀑布流只能垂直滾動,不能水平滾動,所以contentSize.width = 0,咱們只須要計算contentSize.height便可
從字典中找出最長列的最大y值,再加上下面的內邊距,即爲contentSize.height
- (CGSize)collectionViewContentSize {
//假設第0列是最長的那列
__block NSNumber *maxIndex = @0;
//遍歷字典,找出最長的那一列
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
//若是maxColumn列的最大y值小於obj,則讓maxColumn等於obj所屬的列
if
([self.maxYDic[maxIndex] floatValue] < obj.floatValue) {
maxIndex = key;
}
}];
//collectionView的contentSize.height就等於最長列的最大y值+下內邊距
return
CGSizeMake(0, [self.maxYDic[maxIndex] floatValue] + self.sectionInset.bottom);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 方法
該方法則用來設置每一個item的attributes,在這裏,咱們只須要簡單的設置每一個item的attributes.frame便可
首先咱們必須得知collectionView的尺寸,而後咱們根據collectionView的寬度,以及列數、各個間距來計算每一個item的寬度
item的寬度 = (collectionView的寬度 - 內邊距及列邊距) / 列數
CGFloat collectionViewWidth = self.collectionView.frame.size.width;
//self.sectionInset.left:左邊距 self.sectionInset.right:右邊距
//(self.columnCount - 1) * columnSpacing:一行中全部的列邊距
CGFloat itemWidth = (collectionViewWidth - self.sectionInset.left - self.sectionInset.right - (self.columnCount - 1) * self.columnSpacing) / self.columnCount;
接下來計算item的座標,要想計算座標,那就必須知道最短的那一列,先遍歷字典,找出最短列是哪一列(minColumn)以及其最大y值。
item的y值就等於最短列的最大y值再加上行間距,x值就等於左邊距 + (item寬度 + 列間距) * minColumn
//找出最短的那一列
__block NSNumber *minIndex = @0;
[self.maxYDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSNumber *obj, BOOL *stop) {
if
([self.maxYDic[minIndex] floatValue] > obj.floatValue) {
minIndex = key;
}
}];
//根據最短列的列數計算item的x值
CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;
//item的y值 = 最短列的最大y值 + 行間距
CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;
接下來即是item的高度,咱們應該根據圖片的原始尺寸以及計算出來的寬度,等比例縮放來計算高度,可是在layout類中,咱們是拿不到圖片的,所以咱們能夠定義一個block屬性,或者代理,讓外界來計算並返回給咱們,咱們須要將item的寬度以及indexPath傳遞給外界:
@property (nonatomic, strong) CGFloat(^itemHeightBlock)(CGFloat itemHeight,NSIndexPath *indexPath);
根據返回值來設置item的高度:
if
(self.itemHeightBlock) itemHeight = self.itemHeightBlock(itemWidth, indexPath);
最後設置attributes的frame並更新字典:
//設置attributes的frame
attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);
//更新字典中的最短列的最大y值
self.maxYDic[minIndex] = @(CGRectGetMaxY(attributes.frame));
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 方法
該方法用來返回rect範圍內,item的attributes
直接返回attributesArray便可
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return
self.attributesArray;
}
使用
佈局類寫完了,接下來就能夠直接使用了
//建立佈局對象
XRWaterfallLayout *waterfall = [[XRWaterfallLayout alloc] init];
//設置相關屬性
waterfall.columnCount = 3;
//共多少列
waterfall.columnSpacing = 10;
//列間距
waterfall.rowSpacing = 10;
//行間距
waterfall.sectionInset = UIEdgeInsetsMake(10, 10 , 10, 10);
//內邊距
[waterfall setItemHeightBlock:^CGFloat(CGFloat itemWidth, NSIndexPath *indexPath) {
//根據圖片的原始尺寸,及顯示寬度,等比例縮放來計算顯示高度
XRImage *image = self.images[indexPath.item];
return
image.imageH / image.imageW * itemWidth;
}];
collectionView.collectionViewLayout = waterfall;