/**  GWWaterFlowView.h **/
#import <UIKit/UIKit.h>
typedef enum {
    GWWaterflowViewMarginTypeTop,
    GWWaterflowViewMarginTypeBottom,
    GWWaterflowViewMarginTypeLeft,
    GWWaterflowViewMarginTypeRight,
    GWWaterflowViewMarginTypeColumn, // 每一列
    GWWaterflowViewMarginTypeRow, // 每一行
} GWWaterflowViewMarginType;

@class GWWaterflowView, GWWaterFallCell;

/**
 *  数据源方法
 */
@protocol GWWaterflowViewDataSource <NSObject>
@required
/**
 *  一共有多少个数据
 */
- (NSUInteger)numberOfCellsInWaterflowView:(GWWaterflowView *)waterflowView;
/**
 *  返回index位置对应的cell
 */
- (GWWaterFallCell *)waterflowView:(GWWaterflowView *)waterflowView cellAtIndex:(NSUInteger)index;

@optional
/**
 *  一共有多少列
 */
- (NSUInteger)numberOfColumnsInWaterflowView:(GWWaterflowView *)waterflowView;
@end

/**
 *  代理方法
 */
@protocol GWWaterflowViewDelegate <UIScrollViewDelegate>
@optional
/**
 *  第index位置cell对应的高度
 */
- (CGFloat)waterflowView:(GWWaterflowView *)waterflowView heightAtIndex:(NSUInteger)index;
/**
 *  选中第index位置的cell
 */
- (void)waterflowView:(GWWaterflowView *)waterflowView didSelectAtIndex:(NSUInteger)index;
/**
 *  返回间距
 */
- (CGFloat)waterflowView:(GWWaterflowView *)waterflowView marginForType:(GWWaterflowViewMarginType)type;
@end

/**
 *  瀑布流控件
 */
@interface GWWaterflowView : UIScrollView
/**
 *  数据源
 */
@property (nonatomic, weak) id<GWWaterflowViewDataSource> dataSource;
/**
 *  代理
 */
@property (nonatomic, weak) id<GWWaterflowViewDelegate> delegate;

/**
 *  刷新数据(只要调用这个方法,会重新向数据源和代理发送请求,请求数据)
 */
- (void)reloadData;

/**
 *  根据标识去缓存池查找可循环利用的cell
 */
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
@end;
#import "GWWaterflowView.h"
#import "GWWaterFallCell.h"
#import "UIView+Extension.h"

#define GWWaterflowViewDefaultCellH 70
#define GWWaterflowViewDefaultMargin 8
#define GWWaterflowViewDefaultNumberOfColumns 3

@interface GWWaterflowView()
/*
    添加cellframe的数组
 */
@property (nonatomic, strong) NSMutableArray *cellFrames;
/*
   正在展示的cell
 */
@property (nonatomic, strong) NSMutableDictionary *displayingCells;
/*
    缓存cell
 */
@property (nonatomic, strong) NSMutableSet *reusableCells;
@end

@implementation GWWaterflowView

#pragma mark - 初始化
- (NSMutableArray *)cellFrames
{
    if (_cellFrames == nil) {
        self.cellFrames = [NSMutableArray array];
    }
    return _cellFrames;
}

- (NSMutableDictionary *)displayingCells
{
    if (_displayingCells == nil) {
        self.displayingCells = [NSMutableDictionary dictionary];
    }
    return _displayingCells;
}

- (NSMutableSet *)reusableCells
{
    if (_reusableCells == nil) {
        self.reusableCells = [NSMutableSet set];
    }
    return _reusableCells;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
    }
    return self;
}


-(void)didMoveToSuperview
{
    [super didMoveToSuperview];
    [self reloadData];
}
#pragma mark - 公共接口
/*
   刷新数据

 */
- (void)reloadData
{
    // cell的总数
    NSUInteger numberOfCells = [self.dataSource numberOfCellsInWaterflowView:self];
    
    // 总列数
    NSUInteger numberOfColumns = [self numberOfColumns];
    
    // 间距
    CGFloat topM = [self marginForType:GWWaterflowViewMarginTypeTop];
    CGFloat bottomM = [self marginForType:GWWaterflowViewMarginTypeBottom];
    CGFloat leftM = [self marginForType:GWWaterflowViewMarginTypeLeft];
    CGFloat rightM = [self marginForType:GWWaterflowViewMarginTypeRight];
    CGFloat columnM = [self marginForType:GWWaterflowViewMarginTypeColumn];
    CGFloat rowM = [self marginForType:GWWaterflowViewMarginTypeRow];
    
    // cell的宽度
    CGFloat cellW = (self.width - leftM - rightM - (numberOfColumns - 1) * columnM) / numberOfColumns;
    
    // 用一个C语言数组存放所有列的最大Y值
    CGFloat maxYOfColumns[numberOfColumns];
    for (int i = 0; i<numberOfColumns; i++) {
        maxYOfColumns[i] = 0.0;
    }
    
    // 计算所有cell的frame
    for (int i = 0; i<numberOfCells; i++) {
        // cell处在第几列(最短的一列)
        NSUInteger cellColumn = 0;
        // cell所处那列的最大Y值(最短那一列的最大Y值)
        CGFloat maxYOfCellColumn = maxYOfColumns[cellColumn];
        // 求出最短的一列
        for (int j = 1; j<numberOfColumns; j++) {
            if (maxYOfColumns[j] < maxYOfCellColumn) {
                cellColumn = j;
                maxYOfCellColumn = maxYOfColumns[j];
            }
        }
        
        // 根据代理获取i位置的高度
        CGFloat cellH = [self heightAtIndex:i];
        
        // cell的位置
        CGFloat cellX = leftM + cellColumn * (cellW + columnM);
        CGFloat cellY = 0;
        if (maxYOfCellColumn == 0.0) { // 首行
            cellY = topM;
        } else {
            cellY = maxYOfCellColumn + rowM;
        }
        
        // 添加frame到数组中
        CGRect cellFrame = CGRectMake(cellX, cellY, cellW, cellH);
        [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];
        
        // 更新最短那一列的最大Y值
        maxYOfColumns[cellColumn] = CGRectGetMaxY(cellFrame);
    }
    
    // 设置contentSize
    CGFloat contentH = maxYOfColumns[0];
    for (int j = 1; j<numberOfColumns; j++) {
        if (maxYOfColumns[j] > contentH) {
            contentH = maxYOfColumns[j];
        }
    }
    contentH += bottomM;
    self.contentSize = CGSizeMake(0, contentH);
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    // 向数据源索要对应位置的cell
    NSUInteger numberOfCells = self.cellFrames.count;
    for (int i = 0; i<numberOfCells; i++) {
        // 取出i位置的frame
        CGRect cellFrame = [self.cellFrames[i] CGRectValue];
        
        // 优先从字典中取出i位置的cell
        GWWaterFallCell *cell = self.displayingCells[@(i)];
        
        // 判断i位置对应的frame在不在屏幕上(能否看见)
        if ([self isInScreen:cellFrame]) { // 在屏幕上
            

            if (cell == nil) {
                cell = [self.dataSource waterflowView:self cellAtIndex:i];
                cell.frame = cellFrame;
                [self addSubview:cell];
                
                // 存放到字典中
                self.displayingCells[@(i)] = cell;
            }
        } else {  // 不在屏幕上

            if (cell) {
                // 从scrollView和字典中移除
                [cell removeFromSuperview];
                [self.displayingCells removeObjectForKey:@(i)];
                
                // 存放进缓存池

                [self.reusableCells addObject:cell];
            }
        }
    }
}

- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
{
    __block GWWaterFallCell *reusableCell = nil;
    
    [self.reusableCells enumerateObjectsUsingBlock:^(GWWaterFallCell *cell, BOOL *stop) {
        if ([cell.identifier isEqualToString:identifier]) {
            reusableCell = cell;
            *stop = YES;
        }
    }];
    
    if (reusableCell) { // 从缓存池中移除

        [self.reusableCells removeObject:reusableCell];
    }
    return reusableCell;
}

#pragma mark - 私有方法
/*
   判断一个frame有无显示在屏幕上
 */
- (BOOL)isInScreen:(CGRect)frame
{
    return (CGRectGetMaxY(frame) > self.contentOffset.y) &&
    (CGRectGetMinY(frame) < self.contentOffset.y + self.height);
}

/*
   间距
 */
- (CGFloat)marginForType:(GWWaterflowViewMarginType)type
{
    if ([self.delegate respondsToSelector:@selector(waterflowView:marginForType:)]) {
        return [self.delegate waterflowView:self marginForType:type];
    } else {
        return GWWaterflowViewDefaultMargin;
    }
}
/*
 总列数
 */
- (NSUInteger)numberOfColumns
{
    if ([self.dataSource respondsToSelector:@selector(numberOfColumnsInWaterflowView:)]) {
        return [self.dataSource numberOfColumnsInWaterflowView:self];
    } else {
        return GWWaterflowViewDefaultNumberOfColumns;
    }
}
/*
   高度
 */
- (CGFloat)heightAtIndex:(NSUInteger)index
{
    if ([self.delegate respondsToSelector:@selector(waterflowView:heightAtIndex:)]) {
        return [self.delegate waterflowView:self heightAtIndex:index];
    } else {
        return GWWaterflowViewDefaultCellH;
    }
}

#pragma mark - 事件处理
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (![self.delegate respondsToSelector:@selector(waterflowView:didSelectAtIndex:)]) return;
    
    // 获得触摸点
    UITouch *touch = [touches anyObject];
    //    CGPoint point = [touch locationInView:touch.view];
    CGPoint point = [touch locationInView:self];
    
    __block NSNumber *selectIndex = nil;
    [self.displayingCells enumerateKeysAndObjectsUsingBlock:^(id key, GWWaterFallCell *cell, BOOL *stop) {
        if (CGRectContainsPoint(cell.frame, point)) {
            selectIndex = key;
            *stop = YES;
        }
    }];
    
    if (selectIndex) {
        [self.delegate waterflowView:self didSelectAtIndex:selectIndex.unsignedIntegerValue];
    }
}

@end


Logo

更多推荐