iOS之簡單瀑布流的實現(xiàn)

前言

超簡單的瀑布流實現(xiàn),這里說一下筆者的思路,詳細代碼在這里

效果演示

實現(xiàn)思路

collectionView能實現(xiàn)各中吊炸天的布局,其精髓就在于UICollectionViewLayout,因此我們要自定義一個layout來繼承系統(tǒng)的UICollectionViewLayout,所有工作都在這個類中進行

1.定義所需屬性

瀑布流的思路就是,從上往下,那一列最短,就把下一個item放在哪一列,因此我們需要定義一個字典來記錄每一列的最大y值

每一個item都有一個attributes,因此定義一個數(shù)組來保存每一個item的attributes

我們還必須知道有多少列以及列間距、行間距、section到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;

2.重寫系統(tǒng)方法

我們一共需要重寫4個方法
a.- (void)prepareLayout
b.- (CGSize)collectionViewContentSize
c.- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
d.- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

- (void)prepareLayout 方法

布局前的一些準備工作都在這里進行
初始化字典,有幾列就有幾個鍵值對,key為第幾列,value為列的最大y值,初始值為上內邊距

for (int i = 0; i < self.columnCount; i++) {
    self.maxYDic[@(i)] = @(self.sectionInset.top);
}

創(chuàng)建每個item的attributes,并存入數(shù)組

//根據(jù)collectionView獲取總共有多少個item
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];

//為每一個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];
}
- (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的尺寸,然后我們根據(jù)collectionView的寬度,以及列數(shù)、各個間距來計算每個item的寬度

item的寬度 = (collectionView的寬度 - 內邊距及列邊距) / 列數(shù)


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;
    }
}];

//根據(jù)最短列的列數(shù)計算item的x值
CGFloat itemX = self.sectionInset.left + (self.columnSpacing + itemWidth) * minIndex.integerValue;

//item的y值 = 最短列的最大y值 + 行間距
CGFloat itemY = [self.maxYDic[minIndex] floatValue] + self.rowSpacing;

接下來便是item的高度,我們應該根據(jù)圖片的原始尺寸以及計算出來的寬度,等比例縮放來計算高度,但是在layout類中,我們是拿不到圖片的,因此我們可以定義一個block屬性,或者代理,讓外界來計算并返回給我們,我們需要將item的寬度以及indexPath傳遞給外界

@property (nonatomic, strong) CGFloat(^itemHeightBlock)(CGFloat itemHeight,NSIndexPath *indexPath);

根據(jù)返回值來設置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;
}

使用

布局類寫完了,接下來就可以直接使用了

//創(chuàng)建布局對象
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) {
    //根據(jù)圖片的原始尺寸,及顯示寬度,等比例縮放來計算顯示高度
    XRImage *image = self.images[indexPath.item];
    return image.imageH / image.imageW * itemWidth;
}];
collectionView.collectionViewLayout = waterfall;
......
......

具體代碼請到這里下載https://github.com/codingZero/XRWaterfallLayout,覺得不錯的,請獻上你的star

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 前言 iOS里的UI控件其實沒有幾個,界面基本就是圍繞那么幾個控件靈活展開,最難的應屬UICollectionVi...
    alenpaulkevin閱讀 32,490評論 9 176
  • 實現(xiàn)瀑布流簡單,實現(xiàn)分區(qū)瀑布流,并且每個區(qū)的瀑布流的列數(shù)不一樣且有區(qū)頭和區(qū)尾,就不是太容易了。我嫌麻煩不愿意自己寫...
    ac986bb0e59a閱讀 827評論 2 3
  • 序言 前段時間開發(fā)的時候,需要在tableView上拉的時候實現(xiàn)最底下的cell隨著滑動從左邊移動出來的效果(淘寶...
    sindri的小巢閱讀 10,767評論 18 38
  • 朝夕午陽折射影, 道別心路是非論。 不恐瓊樓不懼海, 他生彼岸似人潮。 生非生,死非死。 烏江春水漸稀薄, 唯心向...
    雨墨春秋閱讀 185評論 0 1
  • 這個和當時整個社會背景有關,在佛教創(chuàng)立以前,古印度當時的人大部分信奉的是婆羅門主義,經(jīng)過了幾百年的發(fā)展,人們開始對...
    Gatsby_anan閱讀 1,158評論 0 0

友情鏈接更多精彩內容