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

来源:互联网 发布:程序员转行干什么 编辑:程序博客网 时间:2024/06/04 19:19

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

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

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

[html] view plaincopy
  1. #import <UIKit/UIKit.h>  
  2. #import <QuartzCore/QuartzCore.h>  
  3. #import "UIImageView+WebCache.h"  
  4. @class QuYouAppWaterFlowCell;  
  5.   
  6. @interface QuYouAppWaterfallView : UIScrollView<UIScrollViewDelegate>{  
  7.     float y1;   
  8.     float y2;   
  9.     float y3;  
  10.     float y4;  
  11.       
  12.     UIViewController     *targetVC; //代理的目标target  
  13.     SEL                   action;   //tagert 执行的方法  
  14.       
  15.       
  16.     NSMutableArray       *imageArray;   
  17.     NSMutableArray       *reuseQueue;  
  18.       
  19.     NSMutableDictionary *_dicReuseCells; //重用的cell  
  20.     NSMutableArray      *_onScreenCells; //重用的cell  
  21.     UILabel             *moreLabel;      //上拉查看更多的标签  
  22.     BOOL                isDownLoading;  
  23.     NSArray             *_images;  
  24. }  
  25. @property (nonatomic ,retain,setter = setImages:)NSArray *images;  
  26. @property (nonatomic, retain) NSMutableDictionary *dicReuseCells;  
  27. @property (nonatomic, retain) NSMutableArray *onScreenCells;  
  28. - (void)setLoad;     //以下这三个函数设置图片的loading状态的  
  29. - (BOOL)downLoading;  
  30. - (void)setEndLoad;  
  31. - (id)initWithFrame:(CGRect)frame target:(UIViewController*)target action:(SEL)act;  
  32.   
  33.   
  34. //获取重用的cell  
  35. - (QuYouAppWaterFlowCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;  
  36.   
  37. //将要移除屏幕的cell添加到可重用列表中  
  38. - (void)addCellToReuseQueue:(QuYouAppWaterFlowCell *)cell;  
  39. - (void)reloadImageViews;  
  40.   
  41. @end  

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

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


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

[html] view plaincopy
  1. const int tagAddition = 100;  
  2. #import "QuYouAppWaterfallView.h"  
  3. #define ACTIVITYVIEWTAG  320  
  4. @implementation QuYouAppWaterfallView  
  5. @synthesize images = _images;  
  6. @synthesize dicReuseCells = _dicReuseCellsonScreenCells = _onScreenCells;  
  7. - (void)dealloc  
  8. {  
  9.     [super dealloc];  
  10.     [_onScreenCells release];  
  11.     [imageArray release];  
  12.     [moreLabel release];  
  13.     [_images release];  
  14.     self.images = nil;  
  15. }  
  16. - (id)initWithFrame:(CGRect)frame target:(UIViewController*)target action:(SEL)act  
  17. {  
  18.     self = [super initWithFrame:frame];  
  19.     if (self) {  
  20.         targetVC = target;  
  21.         action   = act;  
  22.         //_images  = [[NSArray alloc]init];  
  23.         self.showsVerticalScrollIndicator = NO;  
  24.         self.delegate = self;  
  25.     }  
  26.     return self;  
  27. }  
  28.   
  29. //获取重用的cell  
  30. - (QuYouAppWaterFlowCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier{  
  31.     if(identifier == nil || identifier.length ==0)return nil;  
  32.       
  33.     NSMutableArray *arrIndentifier_ = [_dicReuseCells objectForKey:identifier];  
  34.     if(arrIndentifier_ && [arrIndentifier_ isKindOfClass:[NSArray class]] && arrIndentifier_.count > 0){  
  35.         //找到了重用的   
  36.         QuYouAppWaterFlowCell *cell_ = [arrIndentifier_ lastObject];  
  37.         [arrIndentifier_ removeLastObject];  
  38.         return cell_;  
  39.     }  
  40.     return nil;  
  41. }  
  42.   
  43. //将要移除屏幕的cell添加到可重用列表中  
  44. - (void)addCellToReuseQueue:(QuYouAppWaterFlowCell *)cell  
  45. {  
  46.     if(cell.strReuseIndentifier.length == 0) return ;  
  47.       
  48.     if(self.dicReuseCells == nil){  
  49.         self.dicReuseCells = [NSMutableDictionary dictionaryWithCapacity:3];  
  50.         NSMutableArray *arr_ = [NSMutableArray arrayWithObject:cell];  
  51.         [_dicReuseCells setObject:arr_ forKey:cell.strReuseIndentifier];  
  52.     }else  
  53.     {  
  54.         NSMutableArray *arr_ = [_dicReuseCells objectForKey:cell.strReuseIndentifier];  
  55.         if(arr_ == nil){  
  56.             arr_ = [NSMutableArray arrayWithObject:cell];  
  57.             [_dicReuseCells setObject:arr_ forKey:cell.strReuseIndentifier];  
  58.         }  
  59.         else {  
  60.             [arr_ addObject:cell];  
  61.         }  
  62.     }  
  63. }  
  64.   
  65.   
  66. - (void)setImages:(NSArray*)images{  
  67.     if (_images != nil)   
  68.     {  
  69.         [_images release];  
  70.     }  
  71.     _images = [images retain];  
  72.     if(!_onScreenCells) _onScreenCells = [[NSMutableArray alloc]init];  
  73.     float offsetY = 4;  
  74.     if (_images) {  
  75.         if (!imageArray) imageArray = [[NSMutableArray alloc]init];  
  76.         [imageArray removeAllObjects];  
  77.         y1 = offsetYy2 = offsetYy3 = offsetY;          
  78.         for (NSDictionary *picDic in self.images) {  
  79.             //find the smallest y in y1, y2, y3, y4  
  80.             float tempY = y1; int caseValue = 0;  
  81.             if (tempY>y2) { tempY = y2caseValue = 1; }  
  82.             if (tempY>y3) { tempY = y3caseValue = 2; }  
  83.               
  84.             float h = [[picDic objectForKey:@"pic_height"]floatValue]/2;  
  85.             int   x = 5 + caseValue%3*105;  
  86.             //   int   x = 10 + caseValue%2*155;  
  87.             float y = 0;  
  88.             switch (caseValue)   
  89.             {  
  90.                 case 0:  
  91.                     y  = y1;   
  92.                     y1 = y1 + h + 4;  
  93.                     break;    
  94.                 case 1:  
  95.                     y = y2;   
  96.                     y2 = y2+ h + 4;  
  97.                     break;  
  98.                 case 2:  
  99.                     y = y3;  
  100.                     y3 = y3 + h + 4;  
  101.                     break;  
  102.                 default:  
  103.                     break;  
  104.             }  
  105.               
  106.             [imageArray addObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:x], [NSNumber numberWithFloat:y], [NSNumber numberWithFloat:h],[picDic objectForKey:@"pic_url"], nil]];  
  107.             if (y1 -50 > self.frame.size.height && y2-50 > self.frame.size.height && y3 -50 > self.frame.size.height) continue;  
  108.             //  if (y1 -75 > self.frame.size.height && y2-75 > self.frame.size.height ) continue;              
  109.               
  110.               
  111.               
  112.               
  113.             QuYouAppWaterFlowCell* imageView;  
  114.             imageView = [[QuYouAppWaterFlowCell alloc] initWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];  
  115.             [imageView setFrame:CGRectMake(x, y, 100, h)];  
  116.             imageView.tag = tagAddition+[images indexOfObject:picDic];  
  117.             DLog(@"%@",[picDic objectForKey:@"pic_url"]);  
  118.             //            [imageView setImageWithURL:[NSURL URLWithString:[picDic objectForKey:@"pic_url"]]];  
  119.               
  120.             [imageView setImageWithURL:[NSURL URLWithString:[picDic objectForKey:@"pic_url"]] placeholderImage:[UIImage imageNamed:@"adorablePictrue_picbackground"]];  
  121.             [self addSubview:imageView];  
  122.               
  123.             [imageView setUserInteractionEnabled:YES];  
  124.             imageView.backgroundColor = [UIColor whiteColor];//保证在图片未加载出来之前能接受滑动手势  
  125.             imageView.layer.borderWidth = 2;  
  126.             imageView.layer.borderColor = [UIColor whiteColor].CGColor;  
  127.               
  128.             UITapGestureRecognizer *tapOne = [[UITapGestureRecognizer alloc] initWithTarget:targetVC action:action];  
  129.             [imageView addGestureRecognizer:tapOne];  
  130.             [tapOne release];  
  131.             [_onScreenCells addObject:imageView];  
  132.         }  
  133.         float tempY = y1;  
  134.         if (tempY<y2tempY = y2;  
  135.         if (tempY<y3tempY = y3;  
  136.           
  137.         moreLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, tempY, 320, 20)];  
  138.         moreLabel.textColor= [UIColor blackColor];  
  139.         moreLabel.textAlignment = UITextAlignmentCenter;  
  140.         moreLabel.font     = [UIFont systemFontOfSize:14];  
  141.         moreLabel.backgroundColor = [UIColor clearColor];  
  142.         moreLabel.text     = @"上拉看更多...";  
  143.         [self addSubview:moreLabel];  
  144.         [self setContentSize:CGSizeMake(self.frame.size.width, (tempY +20> self.frame.size.height ? tempY +20: self.frame.size.height+1))];  
  145.     }  
  146.     DLog(@"init [self.onScreenCells count]: %d",[self.onScreenCells count]);  
  147.       
  148. }  
  149.   
  150.   
  151. - (void)reloadImageViews{  
  152.     CGPoint offset = self.contentOffset;  
  153.     if (!reuseQueue) {  
  154.         reuseQueue = [NSMutableArray array];  
  155.     }  
  156.     //移掉划出屏幕外的图片  
  157.     NSMutableArray *readyToRemove = [NSMutableArray array];  
  158.     for (QuYouAppWaterFlowCell *view in _onScreenCells) {  
  159.         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){  
  160.             [readyToRemove addObject:view];  
  161.         }  
  162.     }  
  163.     for (QuYouAppWaterFlowCell *view in readyToRemove) {  
  164.         QuYouAppWaterFlowCell *imageView = (QuYouAppWaterFlowCell*)view;  
  165.         [imageView cancelCurrentImageLoad];  
  166.         [_onScreenCells removeObject:view];  
  167.         [view removeFromSuperview];  
  168.         [self addCellToReuseQueue:view];  
  169.     }  
  170.     //遍历图片数组  
  171.     for (NSArray *imageInfo in imageArray) {      
  172.         int   tagIndex = [imageArray indexOfObject:imageInfo];  
  173.         float imageX = [[imageInfo objectAtIndex:0] floatValue];            //图片原点x  
  174.         float imageY = [[imageInfo objectAtIndex:1] floatValue];            //图片原点y  
  175.         float imageYH = imageY + [[imageInfo objectAtIndex:2] floatValue];  
  176.           
  177.         BOOL OnScreen = FALSE;  
  178.         if (imageY <= offset.y && imageYH >= offset.y) OnScreen = TRUE;  
  179.         if (imageY >= offset.y && imageY <= (offset.y + self.frame.size.height)) OnScreen = TRUE;  
  180.         //在屏幕范围内的创建添加  
  181.         if (OnScreen) {  
  182.             BOOL HasOnScreen = FALSE;  
  183.             for (QuYouAppWaterFlowCell *vi in _onScreenCells) {  
  184.                 if (tagIndex+tagAddition == vi.tag)HasOnScreen = TRUE;  
  185.             }  
  186.             if (!HasOnScreen) {  
  187.                 QuYouAppWaterFlowCell *imageView = [self dequeueReusableCellWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];  
  188.                 if(imageView == nil)  
  189.                 {  
  190.                     imageView  = [[QuYouAppWaterFlowCell alloc] initWithIdentifier:@"QuYouAppWaterFlowCell_Identifier"];  
  191.                     [imageView setUserInteractionEnabled:YES];  
  192.                     imageView.backgroundColor = [UIColor blackColor];//保证在图片未加载出来之前能接受滑动手势  
  193.                     imageView.layer.borderWidth = 2;  
  194.                     imageView.layer.borderColor = [UIColor whiteColor].CGColor;  
  195.                       
  196.                     UITapGestureRecognizer *tapOne = [[UITapGestureRecognizer alloc] initWithTarget:targetVC action:action];  
  197.                     [imageView addGestureRecognizer:tapOne];  
  198.                     [tapOne release];  
  199.                 }  
  200.                 else   
  201.                 {  
  202.                     //NSLog(@"此条是从重用列表中获取的。。。。。");  
  203.                     [imageView setImage:nil];  
  204.                 }  
  205.                 [imageView setFrame:CGRectMake(imageX, imageY, 100, imageYH-imageY)];  
  206.                 imageView.tag = tagIndex+tagAddition;  
  207.                   
  208.                 [imageView setImageWithURL:[NSURL URLWithString:[imageInfo lastObject]]];  
  209.                 [self addSubview:imageView];  
  210.                 [_onScreenCells addObject:imageView];  
  211.             }  
  212.         }  
  213.     }  
  214.       
  215. }  
  216. - (void)setLoad  
  217. {  
  218.     UIActivityIndicatorView *acitivityView = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];  
  219.     [acitivityView startAnimating];  
  220.     [acitivityView setFrame:CGRectMake(50, 2, 16, 16)];  
  221.     acitivityView.tag = ACTIVITYVIEWTAG;  
  222.     [moreLabel addSubview:acitivityView];  
  223.     [acitivityView release];  
  224.     isDownLoading = YES;  
  225. }  
  226. - (BOOL)downLoading  
  227. {  
  228.     return isDownLoading;   
  229. }  
  230. - (void)setEndLoad  
  231. {  
  232.     UIActivityIndicatorView *activityView = (UIActivityIndicatorView*)[moreLabel viewWithTag:ACTIVITYVIEWTAG];  
  233.     [activityView stopAnimating];  
  234.     [activityView removeFromSuperview];  
  235.     isDownLoading = NO;  
  236. }  
  237. #if 0  
  238. - (void)scrollViewDidScroll:(UIScrollView *)scrollView{  
  239.     [self reloadImageViews];  
  240. }  
  241. - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView  
  242. {  
  243.     [self reloadImageViews];  
  244. }  
  245. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{  
  246. }  
  247. #endif  
  248. @end  
  249.   
  250. //-------------------------------------------------------------------------------------------------------------------------------  
  251. //  
  252. //  
  253. //QuYouAppWaterFlowCell  
  254. //  
  255. //-------------------------------------------------------------------------------------------------------------------------------  
  256. @implementation QuYouAppWaterFlowCell  
  257. @synthesize indexPath = _indexPath;  
  258. @synthesize strReuseIndentifier = _strReuseIndentifier;  
  259.   
  260. -(id)initWithIdentifier:(NSString *)indentifier  
  261. {  
  262.     if(self = [super init])  
  263.     {  
  264.         self.strReuseIndentifier = indentifier;  
  265.     }  
  266.       
  267.     return self;  
  268. }  
  269. @end  

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


3.总结

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

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


原创粉丝点击