• 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
  • 相关阅读:
    Ubuntu 16.04 compare 软件安装
    ubuntu 18 常用软件安装
    LSTM时间序列预测学习
    ubuntu 16.04 屏幕截图
    ubuntu 16.04 tensorboard 学习
    ubuntu 16 .04常见指令整理
    ABAP 更改销售订单(BAPI'BAPI_SALESORDER_CHANGE')
    ABAP SM30表维护生成器,新加一列描述仅供用户维护时参考,不存内表。(例如物料描述,客户描述)
    93年到底多少岁
    一个93年的中年人对2019年的总结
  • 原文地址:https://www.cnblogs.com/DarbyCJ/p/4649399.html
Copyright © 2020-2023  润新知