1. 程式人生 > >WaterfallFlowLayout瀑布流用重寫UICollectionViewFlowLayout類實現

WaterfallFlowLayout瀑布流用重寫UICollectionViewFlowLayout類實現

最近調研瀑布流,在gitHub上下了個Demo發現它的所有檢視都是用Main.storyboard拖的, 自己研究半天沒研究明白;

然後就又找了一個Demo, 它的檢視全是手打的, 但是實現的方法不太好,就將這倆Demo結合了一下:

用了gitHub的實現原理 和 另一個Demo的檢視.

實現瀑布流最重要的一步就是重寫UICollectionViewFlowLayout類, 下面就簡單介紹一下實現原理

本方法實現的僅是高度不一樣, 寬度是根據屏寬平均分的;

當建立UICollectionView的UICollectionViewFlowLayout屬性時,給它傳進去了兩個屬性: 列數, 和需要顯示的Model陣列 (這裡是商品model(圖片,價錢))

WaterfallFlowLayout.h

//  WaterfallFlowLayout.h
@interface WaterfallFlowLayout : UICollectionViewFlowLayout

// 總列數
@property (nonatomic, assign) NSInteger columnCount;

// 商品資料陣列
@property (nonatomic, strong) NSArray *goodsArray;

@end

在.m檔案裡就根據這兩個屬性計算每個frame的大小, 用一個數組記錄每列的高度, 每次計算frame時就將它放到最短列下面:

僅為計算item屬性陣列  和 itemSize, 然後在layoutAttributesForElementsInRect返回了該陣列,就算是改變了每個item的frame了

<span style="font-family: Arial, Helvetica, sans-serif;">//  WaterfallFlowLayout.m</span>
#import "WaterfallFlowLayout.h"
#import "Good.h"

@interface WaterfallFlowLayout ()
// 所有item的屬性的陣列
@property (nonatomic, strong) NSArray *layoutAttributesArray;
@end

@implementation WaterfallFlowLayout

/**
 *  佈局準備方法 當collectionView的佈局發生變化時 會被呼叫
 *  通常是做佈局的準備工作 itemSize.....
 *  UICollectionView 的 contentSize 是根據 itemSize 動態計算出來的
 */
- (void)prepareLayout {
    // 根據列數 計算item的寬度 寬度是一樣的
    CGFloat contentWidth = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right; //減去分割槽的邊框
    CGFloat marginX = self.minimumInteritemSpacing;  //最小左右間距
    CGFloat itemWidth = (contentWidth - marginX * (self.columnCount - 1)) / self.columnCount;
    
    // 計算佈局屬性
    [self computeAttributesWithItemWidth:itemWidth];
}

#pragma mark 根據itemWidth計算佈局屬性
- (void)computeAttributesWithItemWidth:(CGFloat)itemWidth {
    
    // 定義一個列高陣列 記錄每一列的總高度
    CGFloat columnHeight[self.columnCount];
    // 初始化
    for (int i = 0; i < self.columnCount; i++) {
        columnHeight[i] = self.sectionInset.top;
    }
    
    // 遍歷 goodsList 陣列計算相關的屬性
    NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:self.goodsArray.count];
    
    //因為item數是跟當前的商品數一樣的 每次改變都動態計算一遍
    for (NSInteger i = 0; i < self.goodsArray.count; i++) {
        Good *good = self.goodsArray[i];
        // 建立佈局屬性
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        // 獲得當前item的佈局屬性
        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        // 找出最短列號
        NSInteger column = [self shortestColumn:columnHeight];
        // X值
        CGFloat itemX = (itemWidth + self.minimumInteritemSpacing) * column + self.sectionInset.left;
        // Y值 = 當前列的總高度
        CGFloat itemY = columnHeight[column];
        // 等比例縮放 計算item的高度
        CGFloat itemH = good.h * itemWidth / good.w;
        // 設定frame
        attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemH);
        [attributesArray addObject:attributes];
        
        //!!!!!!!!
        self.itemSize = CGSizeMake(itemWidth, itemH);

        // 累加當前列高
        columnHeight[column] += itemH + self.minimumLineSpacing;
        
    }
    // 給屬性陣列設定數值
    self.layoutAttributesArray = attributesArray.copy;
}

#pragma mark 找出columnHeight陣列中最短列號 追加資料的時候追加在最短列中
- (NSInteger)shortestColumn:(CGFloat *)columnHeight {
    
    CGFloat min = CGFLOAT_MAX;
    NSInteger column = 0;
    // 迴圈列高陣列
    for (int i = 0; i < self.columnCount; i++) {
        if (columnHeight[i] < min) {
            min = columnHeight[i];
            column = i;
        }
    }
    return column;
}

/**
 *  跟蹤效果:當到達要顯示的區域時 會計算所有顯示item的屬性
 *           一旦計算完成 所有的屬性會被快取 不會再次計算
 *  @return 返回佈局屬性(UICollectionViewLayoutAttributes)陣列
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    // 直接返回計算好的佈局屬性陣列
    return self.layoutAttributesArray;
}


一下是Demo下載地址:

http://download.csdn.net/detail/margaret_mo/9417425