首先, 对于瀑布流的实现大体分为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返回多高,边界距离, 如果没有实现代理方法,那么返回默认宽高,边界距离, 不依赖于任何数据模型
时间: 2024-11-05 11:41:22