iOS 瀑布流效果(模仿UITableView重用机制)

来源:互联网 发布:算法分析作业答案 编辑:程序博客网 时间:2024/05/10 20:56
瀑布流:
由很多的格子组成,但是每个格子的宽度和高速都是不确定的,是在动态改变的,就像瀑布一样,是一条线一条线的。说明:使用tableView不能实现瀑布流式的布局,因为tableView是以行为单位的,它要求每行(cell)的高度在内部是一致的。本文章介绍了如何自定义一个瀑布流控件来展示信息,本文介绍模仿UITableView的做法自定义瀑布流包括可重用机制的实现。
                 

接口的实现:

接口的实现包括数据源代理和自身代理所需要用到的方法,我们模仿官方的UITableView可以写出自定义View的代理方法:

#import <UIKit/UIKit.h>typedef enum {    JYHWaterfallFlowViewMarginTypeTop,    JYHWaterfallFlowViewMarginTypeBottom,    JYHWaterfallFlowViewMarginTypeLeft,    JYHWaterfallFlowViewMarginTypeRight,    JYHWaterfallFlowViewMarginTypeRow,    JYHWaterfallFlowViewMarginTypeColumn}JYHWaterfallFlowViewMarginType;@class JYHWaterfallFlowView, JYHWaterfallFlowCell;/** *  数据源代理协议 */@protocol JYHWaterfallFlowDataSource<NSObject>@required/** *  该View共有多少个数据 */- (NSUInteger)numberOfCellsInWaterfallFlowView:(JYHWaterfallFlowView *)waterfallFlowView;/** *  返回对应位置的cell */- (JYHWaterfallFlowCell *)waterfallFlowView:(JYHWaterfallFlowView *)waterfallFlowView cellAtIndex:(NSUInteger)index;@optional/** * 该瀑布流要显示几列 */- (NSUInteger)numberOfColumnsInWaterfallFlowView:(JYHWaterfallFlowView *)waterflowFlowView;@end/** *  JYHWaterfallFlowView代理 */@protocol JYHWaterfallFlowDelegate <UIScrollViewDelegate>@optional/** *  第index位置cell对应的高度 */- (CGFloat)waterfallFlowView:(JYHWaterfallFlowView *)waterfallFlowView heightAtIndex:(NSUInteger)index;/** *  选中第index位置的cell */- (void)waterfallFlowView:(JYHWaterfallFlowView *)waterfallFlowView didSelectAtIndex:(NSUInteger)index;/** *  返回间距 */- (CGFloat)waterfallFlowView:(JYHWaterfallFlowView *)waterflowFlowView marginForType:(JYHWaterfallFlowViewMarginType)type;@end@interface JYHWaterfallFlowView : UIScrollView/** *  数据源代理 */@property (nonatomic, weak)id<JYHWaterfallFlowDataSource>dataSource;/** *  JYHWaterfallFlowView代理 */@property (nonatomic, weak)id<JYHWaterfallFlowDelegate>waterfallFlowViewdalegate;/** *  刷新数据方法 */- (void)reloadData;/** *  根据标示符从缓存池中查找可重用的Cell * *  @param identifier 标示符 * *  @return cell */- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;@end
这里需要注意dalegate的命名由于它的父类UIScrollView已经有个叫dalegate的属性了,这里我不小心调了好长时间。。

接下来实现reloadData方法,该方法主要是求出每个cell的Frame里面有一些小算法在里面:

- (void)reloadData {    //cell的总数    long numberOfcells = [self.dataSource numberOfCellsInWaterfallFlowView:self];    //View的总列数    long numberOfColumn = [self numberOfColumn];        //间距    CGFloat topM = [self marginForType:JYHWaterfallFlowViewMarginTypeTop];    CGFloat rightM = [self marginForType:JYHWaterfallFlowViewMarginTypeRight];    CGFloat bottomM = [self marginForType:JYHWaterfallFlowViewMarginTypeBottom];    CGFloat leftM = [self marginForType:JYHWaterfallFlowViewMarginTypeLeft];    CGFloat columnM = [self marginForType:JYHWaterfallFlowViewMarginTypeColumn];    CGFloat rowM = [self marginForType:JYHWaterfallFlowViewMarginTypeRow];        //cell的宽度    CGFloat cellW = (self.bounds.size.width - (rightM + leftM) - (numberOfColumn - 1) * columnM) / numberOfColumn;        // 用一个C语言数组存放所有列的最大Y值    CGFloat maxYOfColumns[numberOfColumn];    for (int i = 0; i<numberOfColumn; i++) {        maxYOfColumns[i] = 0.0;    }        for (int i = 0; i < numberOfcells; i++) {        //cell该放到那一列(最短的那一列),默认为第一列        NSUInteger cellColumn = 0;        //最短一列的最大Y值        CGFloat maxOfcellColumn = maxYOfColumns[cellColumn];        //求最短的一列和最短列的最大Y值        for (int j = 1; j < numberOfColumn; j++) {            if(maxYOfColumns[j] < maxOfcellColumn) {                cellColumn = j;                maxOfcellColumn = maxYOfColumns[j];            }        }        CGFloat cellH = [self hightOfIndex:i];        CGFloat cellX = leftM + cellColumn * (cellW + columnM);        CGFloat cellY = 0.0;        if (maxOfcellColumn == 0.0) {//首行            cellY = topM;        } else  {            cellY = rowM + maxOfcellColumn;        }                CGRect cellFrame = CGRectMake(cellX, cellY, cellW, cellH);        [self.cellFrames addObject:[NSValue valueWithCGRect:cellFrame]];        //更新最短列的最大Y值        maxYOfColumns[cellColumn] = CGRectGetMaxY(cellFrame);    }    //设置scrollView的ContentSize    CGFloat contentH = maxYOfColumns[0];    for (int j = 1; j<numberOfColumn; j++) {        if (maxYOfColumns[j] > contentH) {            contentH = maxYOfColumns[j];        }    }    contentH += bottomM;    self.contentSize = CGSizeMake(0, contentH);}

接下来是重点了,重用cell,将不在屏幕上的cell放入到缓存池中,由于scrollView在滚动过程中会调用layoutSubviews这个方法,所以我们可以在这个方法里面进行一些重用操作:

/** *  当ScrollView滚动时会调用这个方法 */- (void)layoutSubviews {    [super layoutSubviews];        NSUInteger numberOfCells = [self.cellFrames count];    for (int i = 0; i < numberOfCells; i++) {        //取出cellFrame        CGRect cellFrame = [self.cellFrames[i] CGRectValue];        //优先取出字典中的cell        JYHWaterfallFlowCell *cell = self.displayingCells[@(i)];        if([self isInScreen:cellFrame]) {//在屏幕上出现            if(cell == nil) {                cell = [self.dataSource waterfallFlowView:self cellAtIndex:i];                cell.frame = cellFrame;                [self addSubview:cell];                //存到字典中                self.displayingCells[@(i)] = cell;            }        } else {            if (cell) {                [cell removeFromSuperview];                [self.displayingCells removeObjectForKey:@(i)];                                //将cell放入到缓存池中                [self.reusableSet addObject:cell];            }        }    }}

用访问标识从缓存池中取出cell:

-(id)dequeueReusableCellWithIdentifier:(NSString *)identifier {    __block JYHWaterfallFlowCell *resuableCell;    [self.reusableSet enumerateObjectsUsingBlock:^(JYHWaterfallFlowCell *cell, BOOL *stop) {        if([cell.identifier isEqualToString:identifier]) {            resuableCell = cell;            *stop = YES;        }    }];    if(resuableCell) {//用完后将cell从缓存池中移除,防止越积越多        [self.reusableSet removeObject:resuableCell];    }    return resuableCell;}

记得在这个waterfallFlowView加入到父控件时像UITableView一样刷新数据:

- (void)willMoveToSuperview:(UIView *)newSuperview {    [self reloadData];}

当然还有一些懒加载及一些小方法,在这里就不一一列出了,我把demo上传了,想完全了解的可以下载下来看看,本demo只有第一幅图的代码,第二幅图也就是自定义cell往cell里加东西而已。

Demo连接:http://download.csdn.net/detail/u013672551/9212271


0 0
原创粉丝点击