IOS如何实现灵活的瀑布流(2)

         上篇说到瀑布流,我是用的UITableView来实现的,因为在这样同列的图片UITableView有天然的优势,主要是计算图片的位置非常方便,同时能重用减少了不少的工作量。2个月前我做了一个类似于美丽说的产品,其中主要的逻辑就是来做一个瀑布流。

       思路很自然,就是模仿UItableView内存重用的机制。

1。首先设计你的类,这个类提供的接口就是你的图片的地址的集合,毕竟瀑布流很少去读本地的数据,通常是异步去网络请求图片数据,

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "UIImageView+WebCache.h"
@class QuYouAppWaterFlowCell;

@interface QuYouAppWaterfallView : UIScrollView<UIScrollViewDelegate>{
    float y1; 
    float y2; 
    float y3;
    float y4;
    
    UIViewController     *targetVC; //代理的目标target
    SEL                   action;   //tagert 执行的方法
    
    
    NSMutableArray       *imageArray; 
    NSMutableArray       *reuseQueue;
    
    NSMutableDictionary *_dicReuseCells; //重用的cell
    NSMutableArray      *_onScreenCells; //重用的cell
    UILabel             *moreLabel;      //上拉查看更多的标签
    BOOL                isDownLoading;
    NSArray             *_images;
}
@property (nonatomic ,retain,setter = setImages:)NSArray *images;
@property (nonatomic, retain) NSMutableDictionary *dicReuseCells;
@property (nonatomic, retain) NSMutableArray *onScreenCells;
- (void)setLoad;     //以下这三个函数设置图片的loading状态的
- (BOOL)downLoading;
- (void)setEndLoad;
- (id)initWithFrame:(CGRect)frame target:(UIViewController*)target action:(SEL)act;


//获取重用的cell
- (QuYouAppWaterFlowCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

//将要移除屏幕的cell添加到可重用列表中
- (void)addCellToReuseQueue:(QuYouAppWaterFlowCell *)cell;
- (void)reloadImageViews;

@end

1)最上面的y1,y2,y3,y4是用于处理图片的位置的参数.

2) setImages:这个方法就是来设置图片的地址的集合。


2.下面介绍这些接口的实现方式

const int tagAddition = 100;
#import "QuYouAppWaterfallView.h"
#define ACTIVITYVIEWTAG  320
@implementation QuYouAppWaterfallView
@synthesize images = _images;
@synthesize dicReuseCells = _dicReuseCells, onScreenCells = _onScreenCells;
- (void)dealloc
{
    [super dealloc];
    [_onScreenCells release];
    [imageArray release];
    [moreLabel release];
    [_images release];
    self.images = nil;
}
- (id)initWithFrame:(CGRect)frame target:(UIViewController*)target action:(SEL)act
{
    self = [super initWithFrame:frame];
    if (self) {
        targetVC = target;
        action   = act;
        //_images  = [[NSArray alloc]init];
        self.showsVerticalScrollIndicator = NO;
        self.delegate = self;
    }
    return self;
}

//获取重用的cell
- (QuYouAppWaterFlowCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier{
	if(identifier == nil || identifier.length ==0)return nil;
    
	NSMutableArray *arrIndentifier_ = [_dicReuseCells objectForKey:identifier];
	if(arrIndentifier_ && [arrIndentifier_ isKindOfClass:[NSArray class]] && arrIndentifier_.count > 0){
		//找到了重用的 
		QuYouAppWaterFlowCell *cell_ = [arrIndentifier_ lastObject];
		[arrIndentifier_ removeLastObject];
		return cell_;
	}
	return nil;
}

//将要移除屏幕的cell添加到可重用列表中
- (void)addCellToReuseQueue:(QuYouAppWaterFlowCell *)cell
{
	if(cell.strReuseIndentifier.length == 0) return ;
	
	if(self.dicReuseCells == nil){
		self.dicReuseCells = [NSMutableDictionary dictionaryWithCapacity:3];
		NSMutableArray *arr_ = [NSMutableArray arrayWithObject:cell];
		[_dicReuseCells setObject:arr_ forKey:cell.strReuseIndentifier];
	}else
    {
		NSMutableArray *arr_ = [_dicReuseCells objectForKey:cell.strReuseIndentifier];
		if(arr_ == nil){
			arr_ = [NSMutableArray arrayWithObject:cell];
			[_dicReuseCells setObject:arr_ forKey:cell.strReuseIndentifier];
		}
		else {
			[arr_ addObject:cell];
		}
	}
}


- (void)setImages:(NSArray*)images{
    if (_images != nil) 
    {
        [_images release];
    }
    _images = [images retain];
    if(!_onScreenCells) _onScreenCells = [[NSMutableArray alloc]init];
    float offsetY = 4;
    if (_images) {
        if (!imageArray) imageArray = [[NSMutableArray alloc]init];
        [imageArray removeAllObjects];
        y1 = offsetY; y2 = offsetY; y3 = offsetY;        
        for (NSDictionary *picDic in self.images) {
            //find the smallest y in y1, y2, y3, y4
            float tempY = y1; int caseValue = 0;
            if (tempY>y2) { tempY = y2; caseValue = 1; }
            if (tempY>y3) { tempY = y3; caseValue = 2; }
            
            float h = [[picDic objectForKey:@"pic_height"]floatValue]/2;
            int   x = 5 + caseValue%3*105;
            //   int   x = 10 + caseValue%2*155;
            float y = 0;
            switch (caseValue) 
            {
                case 0:
                    y  = y1; 
                    y1 = y1 + h + 4;
                    break;  
                case 1:
                    y = y2; 
                    y2 = y2+ h + 4;
                    break;
                case 2:
                    y = y3;
                    y3 = y3 + h + 4;
                    break;
                default:
                    break;
            }
            
            [imageArray addObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:x], [NSNumber numberWithFloat:y], [NSNumber numberWithFloat:h],[picDic objectForKey:@"pic_url"], nil]];
            if (y1 -50 > self.frame.size.height && y2-50 > self.frame.size.height && y3 -50 > self.frame.size.height) continue;
            //  if (y1 -75 > self.frame.size.height && y2-75 > self.frame.size.height ) continue;            
            
            
            
            
            QuYouAppWaterFlowCell* imageView;
            imageView = [[QuYouAppWaterFlowCell alloc] initWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];
            [imageView setFrame:CGRectMake(x, y, 100, h)];
            imageView.tag = tagAddition+[images indexOfObject:picDic];
            DLog(@"%@",[picDic objectForKey:@"pic_url"]);
            //            [imageView setImageWithURL:[NSURL URLWithString:[picDic objectForKey:@"pic_url"]]];
            
            [imageView setImageWithURL:[NSURL URLWithString:[picDic objectForKey:@"pic_url"]] placeholderImage:[UIImage imageNamed:@"adorablePictrue_picbackground"]];
            [self addSubview:imageView];
            
            [imageView setUserInteractionEnabled:YES];
            imageView.backgroundColor = [UIColor whiteColor];//保证在图片未加载出来之前能接受滑动手势
            imageView.layer.borderWidth = 2;
            imageView.layer.borderColor = [UIColor whiteColor].CGColor;
            
            UITapGestureRecognizer *tapOne = [[UITapGestureRecognizer alloc] initWithTarget:targetVC action:action];
            [imageView addGestureRecognizer:tapOne];
            [tapOne release];
            [_onScreenCells addObject:imageView];
        }
        float tempY = y1;
        if (tempY<y2) tempY = y2;
        if (tempY<y3) tempY = y3;
        
        moreLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, tempY, 320, 20)];
        moreLabel.textColor= [UIColor blackColor];
        moreLabel.textAlignment = UITextAlignmentCenter;
        moreLabel.font     = [UIFont systemFontOfSize:14];
        moreLabel.backgroundColor = [UIColor clearColor];
        moreLabel.text     = @"上拉看更多...";
        [self addSubview:moreLabel];
        [self setContentSize:CGSizeMake(self.frame.size.width, (tempY +20> self.frame.size.height ? tempY +20: self.frame.size.height+1))];
    }
    DLog(@"init [self.onScreenCells count]: %d",[self.onScreenCells count]);
    
}


- (void)reloadImageViews{
    CGPoint offset = self.contentOffset;
    if (!reuseQueue) {
        reuseQueue = [NSMutableArray array];
    }
    //移掉划出屏幕外的图片
    NSMutableArray *readyToRemove = [NSMutableArray array];
    for (QuYouAppWaterFlowCell *view in _onScreenCells) {
        if((view.frame.origin.y + view.frame.size.height  - offset.y) <  0.0001 || (view.frame.origin.y - self.frame.size.height - offset.y) > 0.0001){
            [readyToRemove addObject:view];
        }
    }
    for (QuYouAppWaterFlowCell *view in readyToRemove) {
        QuYouAppWaterFlowCell *imageView = (QuYouAppWaterFlowCell*)view;
        [imageView cancelCurrentImageLoad];
        [_onScreenCells removeObject:view];
        [view removeFromSuperview];
        [self addCellToReuseQueue:view];
    }
    //遍历图片数组
    for (NSArray *imageInfo in imageArray) {    
        int   tagIndex = [imageArray indexOfObject:imageInfo];
        float imageX = [[imageInfo objectAtIndex:0] floatValue];            //图片原点x
        float imageY = [[imageInfo objectAtIndex:1] floatValue];            //图片原点y
        float imageYH = imageY + [[imageInfo objectAtIndex:2] floatValue];
        
        BOOL OnScreen = FALSE;
        if (imageY <= offset.y && imageYH >= offset.y) OnScreen = TRUE;
        if (imageY >= offset.y && imageY <= (offset.y + self.frame.size.height)) OnScreen = TRUE;
        //在屏幕范围内的创建添加
        if (OnScreen) {
            BOOL HasOnScreen = FALSE;
            for (QuYouAppWaterFlowCell *vi in _onScreenCells) {
                if (tagIndex+tagAddition == vi.tag)HasOnScreen = TRUE;
            }
            if (!HasOnScreen) {
                QuYouAppWaterFlowCell *imageView = [self dequeueReusableCellWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];
                if(imageView == nil)
                {
                    imageView  = [[QuYouAppWaterFlowCell alloc] initWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];
                    [imageView setUserInteractionEnabled:YES];
                    imageView.backgroundColor = [UIColor blackColor];//保证在图片未加载出来之前能接受滑动手势
                    imageView.layer.borderWidth = 2;
                    imageView.layer.borderColor = [UIColor whiteColor].CGColor;
                    
                    UITapGestureRecognizer *tapOne = [[UITapGestureRecognizer alloc] initWithTarget:targetVC action:action];
                    [imageView addGestureRecognizer:tapOne];
                    [tapOne release];
                }
                else 
                {
                    //NSLog(@"此条是从重用列表中获取的。。。。。");
                    [imageView setImage:nil];
                }
                [imageView setFrame:CGRectMake(imageX, imageY, 100, imageYH-imageY)];
                imageView.tag = tagIndex+tagAddition;
                
                [imageView setImageWithURL:[NSURL URLWithString:[imageInfo lastObject]]];
                [self addSubview:imageView];
                [_onScreenCells addObject:imageView];
            }
        }
    }
    
}
- (void)setLoad
{
    UIActivityIndicatorView *acitivityView = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [acitivityView startAnimating];
    [acitivityView setFrame:CGRectMake(50, 2, 16, 16)];
    acitivityView.tag = ACTIVITYVIEWTAG;
    [moreLabel addSubview:acitivityView];
    [acitivityView release];
    isDownLoading = YES;
}
- (BOOL)downLoading
{
    return isDownLoading; 
}
- (void)setEndLoad
{
    UIActivityIndicatorView *activityView = (UIActivityIndicatorView*)[moreLabel viewWithTag:ACTIVITYVIEWTAG];
    [activityView stopAnimating];
    [activityView removeFromSuperview];
    isDownLoading = NO;
}
#if 0
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
	[self reloadImageViews];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
	[self reloadImageViews];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
}
#endif
@end

//-------------------------------------------------------------------------------------------------------------------------------
//
//
//QuYouAppWaterFlowCell
//
//-------------------------------------------------------------------------------------------------------------------------------
@implementation QuYouAppWaterFlowCell
@synthesize indexPath = _indexPath;
@synthesize strReuseIndentifier = _strReuseIndentifier;

-(id)initWithIdentifier:(NSString *)indentifier
{
	if(self = [super init])
	{
		self.strReuseIndentifier = indentifier;
	}
	
	return self;
}
@end

上面的代码其实已经足够清晰了,核心的函数其实只有2个setImage: 这个函数在你获取所有的图片列表的时候调用,reloadImages 在你滚动的时候不断的去调用,来计算图片的位置,是不要要移除出屏幕。


3.总结

这段代码也不是我原创,我只是在这基础上稍有改进。主要思路就是重用内存。当然即便如此,在实际的使用过程中,可能会存在卡的现象,可能和图片的大小有关或者网络情况。还有一定的可优化的空间,比如在reloadImages这个函数上面可以做一些优化,减少运算的次数。

其次,在使用过程中用到第三方的图片框架叫SDWebImage,当然你也可以使用其他的库。







  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值