iPhone瀑布流实现


在网上有现成的瀑布流实现方法,但我还是犯贱,自己写了一个WaterfallView。




总体思路:

瀑布流视图继承于UIScrollView,在其中创建一个普通的UIView对象waterView,然后再在waterView中创建瀑布流的各个显示图像的元素,即WaterfallCell的对象。

实现方法:

首先实现瀑布流元素类WaterfallCell,这个类可以写得很简单,其中包括一个UIImageView对象,用于显示图片即可。

比如:

WaterfallCell.h:


#import <UIKit/UIKit.h>
#import "ResListItem.h"

@interface WaterfallCell : UIControl
<ResPicLoaderDelegate>
{
    UIImage* image;
    UIImageView* imgView;
    ResListItem* resItem;
}

- (id)initWithResItem:(ResListItem*)item;
其中ResListItem包括item信息,是自己写的,这里不作讲述。

WaterfallCell.m:

#import "WaterfallCell.h"

@implementation WaterfallCell

- (id)initWithResItem:(ResListItem*)item
{
    self = [super initWithFrame:CGRectZero];
    if (self) 
    {
        imgView = [[UIImageView alloc]initWithFrame:CGRectZero];
        [self addSubview:imgView];
        resItem = item;
        ResIconLoader* iconLoader = [resItem resIcon];
        [iconLoader setLoadDelegate:self];
        image =[iconLoader loadIconFromFile];
        [imgView setImage:image];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    CGRect rtClient = [self bounds];
    CGRect imgBound = CGRectMake(rtClient.origin.x + 1, rtClient.origin.y + 1, rtClient.size.width -2, rtClient.size.height - 2);
    
    [imgView setFrame:imgBound];
}

- (void)resIconLoadEnded:(ResIconLoader *)iconLoader
{
    UIImage* img = [iconLoader loadIconFromFile];
    [imgView setImage:img];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

WaterfallView需要知道有多少个元素需要显示。可以借鉴UITableView,通过协议,从UIViewController获取瀑布流元素的个数,并且通过协议,由UIViewController创建各个元素。因此定义瀑布流的协议:

@protocol WaterfallDelegate <NSObject>
@required
- (NSInteger) numberOfRows;
- (WaterfallCell*)cellForRow:(int)row;
@end


WaterfallView.h

#import <UIKit/UIKit.h>
#import "WaterfallCell.h"

@protocol WaterfallDelegate <NSObject>
@required
- (NSInteger) numberOfRows;
- (WaterfallCell*)cellForRow:(int)row;
@end

@interface WaterfallCellRect : NSObject
{
    CGRect cellFrame;
}
@property CGRect cellFrame;

- (id) initWithRect:(CGRect)frame;
@end

@interface WaterfallView : UIScrollView
{
    id<WaterfallDelegate> waterDelegate;
    int cellCount;
    UIView* waterView;
    NSMutableArray* boundsArray;
    
    float totalHeight;
}

@property id<WaterfallDelegate> waterDelegate;
@property (readonly) int cellCount;

- (id)initWithDelgate:(id<WaterfallDelegate>)waterfallDelegate Frame: (CGRect)frame;
- (void) reloadData;

- (CGRect) cellFrame:(int) nIndex;
- (float) cellPosY:(int)nIndex;
- (void)viewSize;
@end


除了得到各个元素以外,还需要知道UIScrollView的高度,以便控制Scroll。另外最重要的是需要知道各个元素的位置。

想了个办法解决这两个问题

在WaterfallView中用一个动态数组boundsArray,保存每个cell的Frame,还有一个成员变量totalHeight,用于保存UIScrollView的高度。 在获取了cell个数之后,初始化这个动态数组,同时可以计算除UIScrollView的高度。

创建各个Cell的Frame

根据cell的index,可以创建或则获取其Frame。如果index小于boundsArray的元素个数,说明这个cell的Frame已经创建,直接从boundsArray中获取Frame。反之,创建一个新的Frame,并添加到boundsArray。具体实现方法:boundsArray

- (float) cellPosY:(int)nIndex
{
    CGRect clientBound = [self bounds];
    float posY = clientBound.origin.y;
    
    if(nIndex > 2)
    {
        WaterfallCellRect* upCellRect = [boundsArray objectAtIndex:nIndex - 3];
        CGRect upRect = upCellRect.cellFrame;
        posY = upRect.origin.y + upRect.size.height;

    }
    return posY;
}

- (CGRect) cellFrame:(int) nIndex
{
    CGRect clientBound = [self bounds];
    
    if(nIndex < [boundsArray count])
    {
        WaterfallCellRect* cellRect = [boundsArray objectAtIndex:nIndex];
        CGRect bound = [cellRect cellFrame];
        return bound;
    }
    
    int nColume = nIndex % 3;
    float cellWidth = clientBound.size.width / 3;
    int nWidth = cellWidth;
    float nCellHeight = arc4random() % nWidth ;
    NSLog(@"cell height = %f", nCellHeight);
    if (nCellHeight < cellWidth * 2 / 3)
    {
        nCellHeight += cellWidth;
    }
    
    float cellPosY = [self cellPosY:nIndex];
    
    CGRect bound = CGRectMake(clientBound.origin.x + nColume * cellWidth, cellPosY, cellWidth, nCellHeight);
    WaterfallCellRect* cellRect = [[WaterfallCellRect alloc]initWithRect:bound];
    [boundsArray addObject:cellRect];
    //计算新添加的Frame的底部是否已经超过了totalHeight,如果超过了,重新设置totalHeight
    if(totalHeight < bound.origin.y + bound.size.height)
    {
        totalHeight = bound.origin.y + bound.size.height;
    }
    
    return bound;

}

在获取了cell个数之后,就可以创建各个cell的Frame,并计算UIScrollView的高度了。具体实现方法:

-(void)viewSize
{
    CGRect scrollViewBounds = [self bounds];
    [boundsArray removeAllObjects ];
    totalHeight = 0;
    float oriHeight = scrollViewBounds.size.height;
    for (int nIndex = 0; nIndex < cellCount; ++nIndex)
    {
        [self cellFrame:nIndex];
    }
    
    if(totalHeight < oriHeight)
    {
        totalHeight = oriHeight;
    }
    scrollViewBounds.size.height = totalHeight;
    [self setContentSize:scrollViewBounds.size];
    
    if(!waterView)
    {
        waterView = [[UIView alloc]initWithFrame:scrollViewBounds];
        [self addSubview:waterView];
    }
    else 
    {
        [waterView setFrame:scrollViewBounds];
    }
}

然后,模仿UITableView,实现reLoadData方法,初始化各个cell即可。

- (void)reloadData
{
    if(!waterDelegate)
        return;
    cellCount = [waterDelegate numberOfRows];
    [self viewSize];
    
    for (int nIdx = 0; nIdx < cellCount; ++nIdx)
    {
        WaterfallCell* cell = [waterDelegate cellForRow:nIdx];
        CGRect cellFrame = [self cellFrame:nIdx];
        [cell setFrame:cellFrame];
        [waterView addSubview:cell];
    }
    
}

最后在实现WaterfallView的初始化方法:

- (id)initWithDelgate:(id<WaterfallDelegate>)waterfallDelegate Frame: (CGRect)frame
{
    self = [super initWithFrame:frame];
    
    if (self) 
    {
        boundsArray = [[NSMutableArray alloc]init];
        waterDelegate = waterfallDelegate;
        cellCount = [waterDelegate numberOfRows];
        [self viewSize];
        [self reloadData];
    }
    return self;
}



终于大功告成,后续的工作,还可以实现各个cell的点击事件等等操作,就根据具体需求,具体实现。

回头看看,其实还是挺容易的,只要将思路


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值