首先, 對於瀑布流的實現大致分爲tableView和collectionView實現兩種, 以collectionView實現最爲簡單. 本文對流行的實現方式進行改進, 減小依賴,增長更多代理方法,增長擴展性佈局
1 // 2 // AYWaterFlowLayout.h 3 // AY瀑布流 4 // 5 // Created by Jasper on 16/1/25. 6 // Copyright © 2016年 Jasper. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 11 @class AYWaterFlowLayout; 12 13 @protocol AYWaterFlowLayoutDelegate <NSObject> 14 @required 15 - (CGFloat)waterflowLayout:(AYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth; 16 17 @optional 18 - (CGFloat)columnCountInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout; 19 - (CGFloat)columnMarginInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout; 20 - (CGFloat)rowMarginInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout; 21 - (UIEdgeInsets)edgeInsetsInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout; 22 @end 23 24 25 @interface AYWaterFlowLayout : UICollectionViewLayout 26 /** 代理 */ 27 @property (nonatomic, weak) id<AYWaterFlowLayoutDelegate> delegate; 28 @end
1 // 2 // AYWaterFlowLayout.m 3 // AY瀑布流 4 // 5 // Created by Jasper on 16/1/25. 6 // Copyright © 2016年 Jasper. All rights reserved. 7 // 8 9 #import "AYWaterFlowLayout.h" 10 11 12 /** 默認的列數 */ 13 static const NSInteger AYDefaultColumnCount = 3; 14 /** 每一列之間的間距 */ 15 static const CGFloat AYDefaultColumnMargin = 10; 16 /** 每一行之間的間距 */ 17 static const CGFloat AYDefaultRowMargin = 10; 18 /** 邊緣間距 */ 19 static const UIEdgeInsets AYDefaultEdgeInsets = {10, 10, 10, 10}; 20 21 22 23 24 25 @interface AYWaterFlowLayout () 26 27 /** 存放全部cell的佈局屬性 */ 28 @property (nonatomic, strong) NSMutableArray *attrsArray; 29 /** 存放全部列的當前高度 */ 30 @property (nonatomic, strong) NSMutableArray *columnHeights; 31 /** 內容的高度 */ 32 @property (nonatomic, assign) CGFloat contentHeight; 33 34 35 - (CGFloat)rowMargin; 36 - (CGFloat)columnMargin; 37 - (NSInteger)columnCount; 38 - (UIEdgeInsets)edgeInsets; 39 40 @end 41 42 43 @implementation AYWaterFlowLayout 44 45 #pragma mark - 常見數據處理 46 - (CGFloat)rowMargin 47 { 48 if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) { 49 return [self.delegate rowMarginInWaterflowLayout:self]; 50 } else { 51 return AYDefaultRowMargin; 52 } 53 } 54 55 - (CGFloat)columnMargin 56 { 57 if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) { 58 return [self.delegate columnMarginInWaterflowLayout:self]; 59 } else { 60 return AYDefaultColumnMargin; 61 } 62 } 63 64 - (NSInteger)columnCount 65 { 66 if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) { 67 return [self.delegate columnCountInWaterflowLayout:self]; 68 } else { 69 return AYDefaultColumnCount; 70 } 71 } 72 73 - (UIEdgeInsets)edgeInsets 74 { 75 if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) { 76 return [self.delegate edgeInsetsInWaterflowLayout:self]; 77 } else { 78 return AYDefaultEdgeInsets; 79 } 80 } 81 82 #pragma mark - 懶加載 83 - (NSMutableArray *)columnHeights 84 { 85 if (!_columnHeights) { 86 _columnHeights = [NSMutableArray array]; 87 } 88 return _columnHeights; 89 } 90 91 - (NSMutableArray *)attrsArray 92 { 93 if (!_attrsArray) { 94 _attrsArray = [NSMutableArray array]; 95 } 96 return _attrsArray; 97 } 98 99 /** 100 * 初始化 101 */ 102 - (void)prepareLayout 103 { 104 [super prepareLayout]; 105 106 self.contentHeight = 0; 107 108 // 清除之前計算的全部高度 109 [self.columnHeights removeAllObjects]; 110 for (NSInteger i = 0; i < self.columnCount; i++) { 111 [self.columnHeights addObject:@(self.edgeInsets.top)]; 112 } 113 114 // 清除以前全部的佈局屬性 115 [self.attrsArray removeAllObjects]; 116 // 開始建立每個cell對應的佈局屬性 117 NSInteger count = [self.collectionView numberOfItemsInSection:0]; 118 for (NSInteger i = 0; i < count; i++) { 119 // 建立位置 120 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 121 // 獲取indexPath位置cell對應的佈局屬性 122 UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath]; 123 [self.attrsArray addObject:attrs]; 124 } 125 } 126 127 /** 128 * 決定cell的排布 129 */ 130 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 131 { 132 return self.attrsArray; 133 } 134 135 /** 136 * 返回indexPath位置cell對應的佈局屬性 137 */ 138 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 139 { 140 // 建立佈局屬性 141 UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 142 143 // collectionView的寬度 144 CGFloat collectionViewW = self.collectionView.frame.size.width; 145 146 // 設置佈局屬性的frame 147 CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount; 148 CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w]; 149 150 // 找出高度最短的那一列 151 NSInteger destColumn = 0; 152 CGFloat minColumnHeight = [self.columnHeights[0] doubleValue]; 153 for (NSInteger i = 1; i < self.columnCount; i++) { 154 // 取得第i列的高度 155 CGFloat columnHeight = [self.columnHeights[i] doubleValue]; 156 157 if (minColumnHeight > columnHeight) { 158 minColumnHeight = columnHeight; 159 destColumn = i; 160 } 161 } 162 163 CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin); 164 CGFloat y = minColumnHeight; 165 if (y != self.edgeInsets.top) { 166 y += self.rowMargin; 167 } 168 attrs.frame = CGRectMake(x, y, w, h); 169 170 // 更新最短那列的高度 171 self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame)); 172 173 // 記錄內容的高度 174 CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue]; 175 if (self.contentHeight < columnHeight) { 176 self.contentHeight = columnHeight; 177 } 178 return attrs; 179 } 180 181 - (CGSize)collectionViewContentSize 182 { 183 // CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue]; 184 // for (NSInteger i = 1; i < self.columnCount; i++) { 185 // // 取得第i列的高度 186 // CGFloat columnHeight = [self.columnHeights[i] doubleValue]; 187 // 188 // if (maxColumnHeight < columnHeight) { 189 // maxColumnHeight = columnHeight; 190 // } 191 // } 192 return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom); 193 } 194 195 @end
設計的思想源於tableView, 每一個item的高度不應由控件自己決定,而是應該由數據決定, 經過代理告訴我每一個item返回多高,邊界距離, 若是沒有實現代理方法,那麼返回默認寬高,邊界距離, 不依賴於任何數據模型ui