iOS-瀑布流框架


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
原文地址:https://www.cnblogs.com/DarbyCJ/p/4649399.html