前段時間, 做一個羡慕, 需要使用到瀑布流! 說道瀑布流, 或許大家都不陌生, 瀑布流的實現也有很多種! 從scrollView 到 tableView 書寫的瀑布流, 然后再到2012年iOS6 蘋果API 新加進的collectionView進行的瀑布流封裝! 確實, 不論是寫起來還是用起來都要方便很多!
由於項目開發中需要使用到很像瀑布流, 本來想着懶省事, 直接搜一個第三方, 可搜了一會, 並沒有搜到有關橫向瀑布流的第三方! 於是就決定自己寫一個吧! 里邊用到的就是UICollecitionViewFlowLayout 的一個UICollectionViewLayoutAttributes 這個屬性!
要明白瀑布流的原理, 寫起來就會方便很多! 我下代碼, 就是喜歡加多注釋, 能夠方便自己去找問題, 也能把當時的思路給記下來! 所以, 代碼看起來, 好多綠色的中文注釋, 或許對大神來說, 很弱! 但是, 能然自己讀懂, 讓大家讀懂就夠了!哈哈, 廢話不多說! 直接上代碼了! 思路還有實現步驟全在代碼里!
.h 生命中只需要傳入一個 你所需要橫向瀑布流的行數, 還有一個model類的數組, model類是你自己定義的, 里邊有圖片的寬高,就可以了!
有什么疑問, 或者寫的不對的地方, 希望提出來, 我虛心學習, 向大家探討探討
1 // 2 // JF HorizontalWaterFlowLayout.h 3 // ArtWorld 4 // 5 // Created by laouhn on 15/10/29. 6 // Copyright © 2015年 Jesonliu. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 11 @interface JF_HorizontalWaterFlowLayout : UICollectionViewFlowLayout 12 13 // 總行數 14 @property (nonatomic, assign) NSInteger rowCount; 15 // 商品數據數組 16 @property (nonatomic, strong) NSArray *modelList; 17 18 @end
1 // 2 // JF HorizontalWaterFlowLayout.m 3 // ArtWorld 4 // 5 // Created by laouhn on 15/10/29. 6 // Copyright © 2015年 Jesonliu. All rights reserved. 7 // 8 9 #import "JF HorizontalWaterFlowLayout.h" 10 #import "ArtCollectionViewCellModel.h" 11 12 @interface JF_HorizontalWaterFlowLayout () 13 @property (nonatomic, assign) CGFloat remmenberW; 14 15 // 所有item的屬性的數組 16 @property (nonatomic, strong) NSArray *layoutAttributesArray; // 定義布局屬性的數組, 用來存放布局item 的屬性 17 18 @end 19 20 @implementation JF_HorizontalWaterFlowLayout 21 /** 22 * 布局准備方法 當collectionView的布局發生變化時 會被調用 23 * 通常是做布局的准備工作 itemSize..... 24 * UICollectionView 的 contentSize 是根據 itemSize 動態計算出來的 25 */ 26 27 - (void)prepareLayout { 28 [super prepareLayout]; 29 // 根據行數 計算item的高度 所有item 的高度是一樣的 30 // 內容頁的高度 31 CGFloat contenHeight = self.collectionView.bounds.size.height - self.sectionInset.top - self.sectionInset.bottom; 32 // 行與行之間的距離 33 CGFloat marginY = self.minimumLineSpacing; 34 // item 的高度 35 CGFloat itemHeight = (contenHeight - marginY * (self.rowCount - 1)) / self.rowCount; 36 // 計算布局屬性 37 [self computeAttributesWithItemWith:itemHeight]; 38 } 39 40 41 /** 42 根據itemHeight 計算布局屬性 43 */ 44 - (void)computeAttributesWithItemWith:(CGFloat)itemHeight { 45 // 定義一個行寬數組 記錄每一行的總寬度 46 CGFloat rowWidth[self.rowCount]; 47 // 定義一個記錄每一行的總item個數數組 48 NSInteger rowItemCount[self.rowCount]; // 此處為C語言格式, 定義兩個數組, 元素個數為self.rowCount, 數組類型為CGFloat 和 NSInteger 49 50 // 初始化 51 for (int i = 0; i < self.rowCount; i++) { 52 rowWidth[i] = self.sectionInset.left; // 行寬 要加上分區距離左邊的距離 所以初始距離 只有分區距左邊的距離 53 rowItemCount[i] = 0; // 初始化時, 是每一行item 的個數為零, 清空數組元素內容 54 } 55 56 // 遍歷modelList 數組, 計算相關屬性 57 NSInteger index = 0; // 定義索引變量, 初始值為零 58 59 NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:self.modelList.count]; // 定義一個可變存儲布局屬性的數組, 並開辟空間大小為self.modelList.count個空間 60 61 // 遍歷self.modelList 數組, 得到model 類, 獲取屬性 62 for (ArtCollectionViewCellModel *model in self.modelList) { 63 64 // 創建路徑, 記錄item 所在分區分區和位置 65 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; 66 67 // 根據路徑創建對應的布局屬性 68 UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 69 70 // 找出最短的行號 71 NSInteger row = [self shortestRow:rowWidth]; 72 73 // 追加在最短行, 使記錄每一行的總item個數數組中最短行對應的元素個數 +1 74 rowItemCount[row]++; 75 76 // X值 77 CGFloat itemX = rowWidth[row]; // 最短行的總寬度 78 79 // Y值 80 CGFloat itemY = (itemHeight + self.minimumLineSpacing) * row + self.sectionInset.top; // 計算, 注意邏輯性 81 82 // 等比例縮放 計算item 的寬度 83 CGFloat itemW = ([model.worksWidth floatValue] / [model.worksHeight floatValue]) * itemHeight; // 顯示寬 / 顯示高 = 實際寬 / 實際高 84 // 賦給布局屬性的frame 85 attributes.frame = CGRectMake(itemX, itemY, itemW, itemHeight); 86 87 // 添加到布局屬性的數組中 88 [attributesArray addObject:attributes]; 89 90 // 使列寬增加, 增量為item 自身的寬度, 加上兩個item 之間的最小距離 91 rowWidth[row] += itemW + self.minimumInteritemSpacing; 92 93 // 是索引+1 94 index++; 95 } 96 97 98 // 找出 最寬行的行號 99 NSInteger row = [self widthestRow:rowWidth]; 100 101 self.remmenberW = rowWidth[row]; 102 103 // 根據最寬行設置itemSize 使用總寬度的平均值 104 CGFloat itemW = (rowWidth[row] - self.minimumInteritemSpacing * rowItemCount[row]) / rowItemCount[row]; 105 106 self.itemSize = CGSizeMake(itemW, itemHeight); 107 108 // 給屬性數組設置數值 109 self.layoutAttributesArray = attributesArray.copy; 110 111 } 112 113 // 找出rowWidth 數組中最短行號 追加數據的時候 追加在最短行中 114 - (NSInteger)shortestRow:(CGFloat *)rowWidth { 115 CGFloat max = CGFLOAT_MAX; 116 NSInteger row = 0; 117 for (int i = 0; i < self.rowCount; i++) { 118 if (rowWidth[i] < max) { 119 max = rowWidth[i]; 120 row = i; 121 } 122 } 123 return row; // 返回最短的行 124 } 125 126 // 找出rowWidth 數組中最寬的行號 127 - (NSInteger)widthestRow:(CGFloat *)rowWidth { 128 CGFloat min = 0; 129 NSInteger row = 0; 130 for (int i = 0; i < self.rowCount; i++) { 131 if (rowWidth[i] > min) { 132 min = rowWidth[i]; 133 row = i; 134 } 135 } 136 return row; 137 } 138 139 140 /** 141 * 跟蹤效果:當到達要顯示的區域時 會計算所有顯示item的屬性 142 * 一旦計算完成 所有的屬性會被緩存 不會再次計算 143 * @return 返回布局屬性(UICollectionViewLayoutAttributes)數組 144 */ 145 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 146 147 // 直接返回計算好的布局屬性數組 148 return self.layoutAttributesArray; 149 } 150 151 // 重寫此方法, 改變collectionView的contentSize 152 - (CGSize )collectionViewContentSize{ 153 return CGSizeMake(self.remmenberW, self.collectionView.bounds.size.height); 154 } 155 156 157 158 159 @end
