瀑布流 核心
·
/** 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
更多推荐


所有评论(0)