CJWaterflowView.h
1 #import <UIKit/UIKit.h>
2
3 typedef enum {
4 CJWaterflowViewMarginTypeTop,
5 CJWaterflowViewMarginTypeBottom,
6 CJWaterflowViewMarginTypeLeft,
7 CJWaterflowViewMarginTypeRight,
8 CJWaterflowViewMarginTypeColumn, // 每一列
9 CJWaterflowViewMarginTypeRow, // 每一行
10 } CJWaterflowViewMarginType;
11
12 @class CJWaterflowView, CJWaterflowViewCell;
13
14 /**
15 * 数据源方法
16 */
17 @protocol CJWaterflowViewDataSource <NSObject>
18 @required
19 /**
20 * 一共有多少个数据
21 */
22 - (NSUInteger)numberOfCellsInWaterflowView:(CJWaterflowView *)waterflowView;
23 /**
24 * 返回index位置对应的cell
25 */
26 - (CJWaterflowViewCell *)waterflowView:(CJWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;
27
28 @optional
29 /**
30 * 一共有多少列
31 */
32 - (NSUInteger)numberOfColumnsInWaterflowView:(CJWaterflowView *)waterflowView;
33 @end
34
35 /**
36 * 代理方法
37 */
38 @protocol CJWaterflowViewDelegate <UIScrollViewDelegate>
39 @optional
40 /**
41 * 第index位置cell对应的高度
42 */
43 - (CGFloat)waterflowView:(CJWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;
44 /**
45 * 选中第index位置的cell
46 */
47 - (void)waterflowView:(CJWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;
48 /**
49 * 返回间距
50 */
51 - (CGFloat)waterflowView:(CJWaterflowView *)waterflowView marginForType:(CJWaterflowViewMarginType)type;
52 @end
53
54 /**
55 * 瀑布流控件
56 */
57 @interface CJWaterflowView : UIScrollView
58 /**
59 * 数据源
60 */
61 @property (nonatomic, weak) id<CJWaterflowViewDataSource> dataSource;
62 /**
63 * 代理
64 */
65 @property (nonatomic, weak) id<CJWaterflowViewDelegate> delegate;
66
67 /**
68 * 刷新数据(只要调用这个方法,会重新向数据源和代理发送请求,请求数据)
69 */
70 - (void)reloadData;
71
72 /**
73 * cell的宽度
74 */
75 - (CGFloat)cellWidth;
76
77 /**
78 * 根据标识去缓存池查找可循环利用的cell
79 */
80 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
81 @end
1 CJWaterflowView.m
2
3
4 #import "CJWaterflowView.h"
5 #import "CJWaterflowViewCell.h"
6
7 #define CJWaterflowViewDefaultCellH 70
8 #define CJWaterflowViewDefaultMargin 8
9 #define CJWaterflowViewDefaultNumberOfColumns 3
10
11 @interface CJWaterflowView()
12 /**
13 * 所有cell的frame数据
14 */
15 @property (nonatomic, strong) NSMutableArray *cellFrames;
16 /**
17 * 正在展示的cell
18 */
19 @property (nonatomic, strong) NSMutableDictionary *displayingCells;
20 /**
21 * 缓存池(用Set,存放离开屏幕的cell)
22 */
23 @property (nonatomic, strong) NSMutableSet *reusableCells;
24 @end
25
26 @implementation CJWaterflowView
27
28 #pragma mark - 初始化
29 - (NSMutableArray *)cellFrames
30 {
31 if (_cellFrames == nil) {
32 self.cellFrames = [NSMutableArray array];
33 }
34 return _cellFrames;
35 }
36
37 - (NSMutableDictionary *)displayingCells
38 {
39 if (_displayingCells == nil) {
40 self.displayingCells = [NSMutableDictionary dictionary];
41 }
42 return _displayingCells;
43 }
44
45 - (NSMutableSet *)reusableCells
46 {
47 if (_reusableCells == nil) {
48 self.reusableCells = [NSMutableSet set];
49 }
50 return _reusableCells;
51 }
52
53 - (id)initWithFrame:(CGRect)frame
54 {
55 self = [super initWithFrame:frame];
56 if (self) {
57
58 }
59 return self;
60 }
61
62 - (void)willMoveToSuperview:(UIView *)newSuperview
63 {
64 [self reloadData];
65 }
66
67 #pragma mark - 公共接口
68 /**
69 * cell的宽度
70 */
71 - (CGFloat)cellWidth
72 {
73 // 总列数
74 int numberOfColumns = [self numberOfColumns];
75 CGFloat leftM = [self marginForType:CJWaterflowViewMarginTypeLeft];
76 CGFloat rightM = [self marginForType:CJWaterflowViewMarginTypeRight];
77 CGFloat columnM = [self marginForType:CJWaterflowViewMarginTypeColumn];
78 return (self.bounds.size.width - leftM - rightM - (numberOfColumns - 1) * columnM) / numberOfColumns;
79 }
80
81 /**
82 * 刷新数据
83 */
84 - (void)reloadData
85 {
86 // 清空之前的所有数据
87 // 移除正在正在显示cell
88 [self.displayingCells.allValues makeObjectsPerformSelector:@selector(removeFromSuperview)];
89 [self.displayingCells removeAllObjects];
90 [self.cellFrames removeAllObjects];
91 [self.reusableCells removeAllObjects];
92
93 // cell的总数
94 int numberOfCells = [self.dataSource numberOfCellsInWaterflowView:self];
95
96 // 总列数
97 int numberOfColumns = [self numberOfColumns];
98
99 // 间距
100 CGFloat topM = [self marginForType:CJWaterflowViewMarginTypeTop];
101 CGFloat bottomM = [self marginForType:CJWaterflowViewMarginTypeBottom];
102 CGFloat leftM = [self marginForType:CJWaterflowViewMarginTypeLeft];
103 CGFloat columnM = [self marginForType:CJWaterflowViewMarginTypeColumn];
104 CGFloat rowM = [self marginForType:CJWaterflowViewMarginTypeRow];
105
106 // cell的宽度
107 CGFloat cellW = [self cellWidth];
108
109 // 用一个C语言数组存放所有列的最大Y值
110 CGFloat maxYOfColumns[numberOfColumns];
111 for (int i = 0; i<numberOfColumns; i++) {
112 maxYOfColumns[i] = 0.0;
113 }
114
115 // 计算所有cell的frame
116 for (int i = 0; i<numberOfCells; i++) {
117 // cell处在第几列(最短的一列)
118 NSUInteger cellColumn = 0;
119 // cell所处那列的最大Y值(最短那一列的最大Y值)
120 CGFloat maxYOfCellColumn = maxYOfColumns[cellColumn];
121 // 求出最短的一列
122 for (int j = 1; j<numberOfColumns; j++) {
123 if (maxYOfColumns[j] < maxYOfCellColumn) {
124 cellColumn = j;
125 maxYOfCellColumn = maxYOfColumns[j];
126 }
127 }
128
129 // 询问代理i位置的高度
130 CGFloat cellH = [self heightAtIndex:i];
131
132 // cell的位置
133 CGFloat cellX = leftM + cellColumn * (cellW + columnM);
134 CGFloat cellY = 0;
135 if (maxYOfCellColumn == 0.0) { // 首行
136 cellY = topM;
137 } else {
138 cellY = maxYOfCellColumn + rowM;
139 }
140
141 // 添加frame到数组中
142 CGRect cellFrame = CGRectMake(cellX, cellY, cellW, cellH);
143 [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
144
145 // 更新最短那一列的最大Y值
146 maxYOfColumns[cellColumn] = CGRectGetMaxY(cellFrame);
147 }
148
149 // 设置contentSize
150 CGFloat contentH = maxYOfColumns[0];
151 for (int j = 1; j<numberOfColumns; j++) {
152 if (maxYOfColumns[j] > contentH) {
153 contentH = maxYOfColumns[j];
154 }
155 }
156 contentH += bottomM;
157 self.contentSize = CGSizeMake(0, contentH);
158 }
159
160 /**
161 * 当UIScrollView滚动的时候也会调用这个方法
162 */
163 - (void)layoutSubviews
164 {
165 [super layoutSubviews];
166
167 // 向数据源索要对应位置的cell
168 NSUInteger numberOfCells = self.cellFrames.count;
169 for (int i = 0; i<numberOfCells; i++) {
170 // 取出i位置的frame
171 CGRect cellFrame = [self.cellFrames[i] CGRectValue];
172
173 // 优先从字典中取出i位置的cell
174 CJWaterflowViewCell *cell = self.displayingCells[@(i)];
175
176 // 判断i位置对应的frame在不在屏幕上(能否看见)
177 if ([self isInScreen:cellFrame]) { // 在屏幕上
178 if (cell == nil) {
179 cell = [self.dataSource waterflowView:self cellAtIndex:i];
180 cell.frame = cellFrame;
181 [self addSubview:cell];
182
183 // 存放到字典中
184 self.displayingCells[@(i)] = cell;
185 }
186 } else { // 不在屏幕上
187 if (cell) {
188 // 从scrollView和字典中移除
189 [cell removeFromSuperview];
190 [self.displayingCells removeObjectForKey:@(i)];
191
192 // 存放进缓存池
193 [self.reusableCells addObject:cell];
194 }
195 }
196 }
197 }
198
199 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
200 {
201 __block CJWaterflowViewCell *reusableCell = nil;
202
203 [self.reusableCells enumerateObjectsUsingBlock:^(CJWaterflowViewCell *cell, BOOL *stop) {
204 if ([cell.identifier isEqualToString:identifier]) {
205 reusableCell = cell;
206 *stop = YES;
207 }
208 }];
209
210 if (reusableCell) { // 从缓存池中移除
211 [self.reusableCells removeObject:reusableCell];
212 }
213 return reusableCell;
214 }
215
216 #pragma mark - 私有方法
217 /**
218 * 判断一个frame有无显示在屏幕上
219 */
220 - (BOOL)isInScreen:(CGRect)frame
221 {
222 return (CGRectGetMaxY(frame) > self.contentOffset.y) &&
223 (CGRectGetMinY(frame) < self.contentOffset.y + self.bounds.size.height);
224 }
225
226 /**
227 * 间距
228 */
229 - (CGFloat)marginForType:(CJWaterflowViewMarginType)type
230 {
231 if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {
232 return [self.delegate waterflowView:self marginForType:type];
233 } else {
234 return CJWaterflowViewDefaultMargin;
235 }
236 }
237 /**
238 * 总列数
239 */
240 - (NSUInteger)numberOfColumns
241 {
242 if ([self.dataSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {
243 return [self.dataSource numberOfColumnsInWaterflowView:self];
244 } else {
245 return CJWaterflowViewDefaultNumberOfColumns;
246 }
247 }
248 /**
249 * index位置对应的高度
250 */
251 - (CGFloat)heightAtIndex:(NSUInteger)index
252 {
253 if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {
254 return [self.delegate waterflowView:self heightAtIndex:index];
255 } else {
256 return CJWaterflowViewDefaultCellH;
257 }
258 }
259
260 #pragma mark - 事件处理
261 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
262 {
263 if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) return;
264
265 // 获得触摸点
266 UITouch *touch = [touches anyObject];
267 // CGPoint point = [touch locationInView:touch.view];
268 CGPoint point = [touch locationInView:self];
269
270 __block NSNumber *selectIndex = nil;
271 [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, CJWaterflowViewCell *cell, BOOL *stop) {
272 if (CGRectContainsPoint(cell.frame, point)) {
273 selectIndex = key;
274 *stop = YES;
275 }
276 }];
277
278 if (selectIndex) {
279 [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue];
280 }
281 }
282
283 @end
1 CJWaterflowViewCell.h
2
3
4 #import <UIKit/UIKit.h>
5
6 @interface CJWaterflowViewCell : UIView
7 @property (nonatomic, copy) NSString *identifier;
8 @end