• lazy懒载入(延迟载入)UITableView


                举个样例,当我们在用网易新闻App时,看着那么多的新闻,并非全部的都是我们感兴趣的,有的时候我们仅仅是非常快的滑过,想要高速的略过不喜欢的内容,可是仅仅要滑动经过了,图片就開始载入了,这样用户体验就不太好,并且浪费内存.

                 这个时候,我们就能够利用lazy载入技术,当界面滑动或者滑动减速的时候,都不进行图片载入,仅仅有当用户不再滑动并且减速效果停止的时候,才进行载入.

                  刚開始我异步载入图片利用SDWebImage来做,最后试验的时候出现了重用bug,由于尽管SDWebImage实现了异步载入缓存,当载入完图片后再请求会直接载入缓存中的图片,注意注意注意,关键的来了,假设是lazy载入,滑动过程中是不进行网络请求的,cell上的图片就会发生重用,当你停下来能进行网络请求的时候,才会变回到当前Cell应有的图片,大概1-2秒的延迟吧(不算延迟,就是没有进行请求,也不是没有缓存的问题).怎么解决呢?

    这个时候我们就要在Model对象中定义个一个UIImage的属性,异步下载图片后,用已经缓存在沙盒中的图片路径给它赋值,这样,才cellForRowAtIndexPath方法中,推断这个UIImage对象是否为空,若为空,就进行网络请求,不为空,就直接将它赋值给cell的imageView对象,这样就能非常好的解决图片短暂重用问题.

                  @以下我的代码用的是自己写的异步载入缓存类,SDWebImage的载入图片的懒载入,会在后面的章节给出.(为什么不同呢,由于SDWebImage我曾经使用重来不关心它将图片存储在沙盒中的名字和路径,可是要实现懒载入的话,一定要得到图片路径,所以在找SDWebImage怎样存储图片路径上花了点时间)

    @model类
    #import <Foundation/Foundation.h>
    
    @interface NewsItem : NSObject
    
    @property (nonatomic,copy) NSString * newsTitle;
    @property (nonatomic,copy) NSString * newsPicUrl;
    @property (nonatomic,retain) UIImage * newsPic; //  存储每一个新闻自己的image对象
    
    - (id)initWithDictionary:(NSDictionary *)dic;
    
    //  处理解析
    + (NSMutableArray *)handleData:(NSData *)data;
    @end
    
    
    #import "NewsItem.h"
    #import "ImageDownloader.h"
    
    @implementation NewsItem
    
    - (void)dealloc
    {
    	self.newsTitle = nil;
    	self.newsPicUrl = nil;
    	self.newsPic = nil;
    	[super dealloc];
    }
    
    - (id)initWithDictionary:(NSDictionary *)dic
    {
    	self = [super init];
    	if (self) {
    
    
    		self.newsTitle = [dic objectForKey:@"title"];
    		self.newsPicUrl = [dic objectForKey:@"picUrl"];
    		
    		//从本地沙盒载入图像
    		ImageDownloader * downloader = [[[ImageDownloader alloc] init] autorelease];
    		self.newsPic = [downloader loadLocalImage:_newsPicUrl];
    
    	}
    
    	return self;
    }
    
    + (NSMutableArray *)handleData:(NSData *)data;
    {
    
    		//解析数据
    		NSError * error = nil;
    		NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
    		NSMutableArray * originalArray = [dic objectForKey:@"news"];
    
    		//封装数据对象
    		NSMutableArray * resultArray = [NSMutableArray array];
    	
    		for (int i=0 ;i<[originalArray count]; i++) {
    			NSDictionary * newsDic = [originalArray objectAtIndex:i];
    			NewsItem * item = [[NewsItem alloc] initWithDictionary:newsDic];
    			[resultArray addObject:item];
    			[item release];
    		}
    
    		return resultArray;
    
    }
    
    @end
    

    @图片下载类
    #import <Foundation/Foundation.h>
    
    
    @class NewsItem;
    
    
    @interface ImageDownloader : NSObject
    
    
    @property (nonatomic,copy) NSString * imageUrl;
    @property (nonatomic,retain) NewsItem * newsItem; //下载图像所属的新闻
    
    
    //图像下载完毕后,使用block实现回调
    @property (nonatomic,copy) void (^completionHandler)(void);
    
    
    //開始下载图像
    - (void)startDownloadImage:(NSString *)imageUrl;
    
    
    //从本地载入图像
    - (UIImage *)loadLocalImage:(NSString *)imageUrl;
    
    
    @end
    
    
    
    
    #import "ImageDownloader.h"
    #import "NewsItem.h"
    
    
    @implementation ImageDownloader
    
    
    - (void)dealloc
    {
    	self.imageUrl = nil;
    	Block_release(_completionHandler);
    	[super dealloc];
    }
    
    
    
    
    #pragma mark - 异步载入
    - (void)startDownloadImage:(NSString *)imageUrl
    {
    
    
    	self.imageUrl = imageUrl;
    
    
    	// 先推断本地沙盒是否已经存在图像,存在直接获取,不存在再下载,下载后保存
    	// 存在沙盒的Caches的子目录DownloadImages中
    	UIImage * image = [self loadLocalImage:imageUrl];
    
    
    	if (image == nil) {
    
    
    		// 沙盒中没有,下载
            // 异步下载,分配在程序进程缺省产生的并发队列
    		dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
    
    			// 多线程中下载图像
    			NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
    
    
                // 缓存图片
    			[imageData writeToFile:[self imageFilePath:imageUrl] atomically:YES];
    
    
                // 回到主线程完毕UI设置
    			dispatch_async(dispatch_get_main_queue(), ^{
    
    
    				//将下载的图像。存入newsItem对象中
    				UIImage * image = [UIImage imageWithData:imageData];
    				self.newsItem.newsPic = image;
    
    
    				//使用block实现回调,通知图像下载完毕
    				if (_completionHandler) {
    					_completionHandler();
    				}
    				
    			});
    			
    		});
    	}
    	
    }
    
    #pragma mark - 载入本地图像
    - (UIImage *)loadLocalImage:(NSString *)imageUrl
    {
    
    	self.imageUrl = imageUrl;
    
    
        // 获取图像路径
    	NSString * filePath = [self imageFilePath:self.imageUrl];
    
    
    	UIImage * image = [UIImage imageWithContentsOfFile:filePath];
    
    
    	if (image != nil) {
    		return image;
    	}
    
    	return nil;
    }
    
    #pragma mark - 获取图像路径
    - (NSString *)imageFilePath:(NSString *)imageUrl
    {
    	// 获取caches目录路径
    	NSString * cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    
    
    	// 创建DownloadImages目录
    	NSString * downloadImagesPath = [cachesPath stringByAppendingPathComponent:@"DownloadImages"];
    	NSFileManager * fileManager = [NSFileManager defaultManager];
    	if (![fileManager fileExistsAtPath:downloadImagesPath]) {
    
    
    		[fileManager createDirectoryAtPath:downloadImagesPath withIntermediateDirectories:YES attributes:nil error:nil];
    	}
    
    
    #pragma mark 拼接图像文件在沙盒中的路径,由于图像URL有"/",要在存入前替换掉,任意用"_"取代
    	NSString * imageName = [imageUrl stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
    	NSString * imageFilePath = [downloadImagesPath stringByAppendingPathComponent:imageName];
    
    
    	return imageFilePath;
    }
    
    @end


    
    @这里仅仅给出关键代码,网络请求,数据处理,自己定义cell自行解决
    
    #pragma mark - Table view data source
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        // Return the number of sections.
        return 1;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        // Return the number of rows in the section.
        if (_dataArray.count == 0) {
            return 10;
        }
        return [_dataArray count];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    	static NSString *cellIdentifier = @"Cell";
    	NewsListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier ];
    	if (!cell) {
    		cell = [[[NewsListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
    	}
    
    	NewsItem * item = [_dataArray objectAtIndex:indexPath.row];
    
    	cell.titleLabel.text = item.newsTitle;
    
    	//推断将要展示的新闻有无图像
    
    	if (item.newsPic == nil) {
    		//没有图像下载
    		cell.picImageView.image = nil;
            
            NSLog(@"dragging = %d,decelerating = %d",self.tableView.dragging,self.tableView.decelerating);
            // ??运行的时机与次数问题
    		if (self.tableView.dragging == NO && self.tableView.decelerating == NO) {
    			[self startPicDownload:item forIndexPath:indexPath];
    		}
    
    	}else{
    		//有图像直接展示
            NSLog(@"1111");
    		cell.picImageView.image = item.newsPic;
    
    	}
        
        cell.titleLabel.text = [NSString stringWithFormat:@"indexPath.row = %ld",indexPath.row];
    
    	return cell;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    	return [NewsListCell cellHeight];
    }
    
    //開始下载图像
    - (void)startPicDownload:(NewsItem *)item forIndexPath:(NSIndexPath *)indexPath
    {
    	//创建图像下载器
    	ImageDownloader * downloader = [[ImageDownloader alloc] init];
    
    	//下载器要下载哪个新闻的图像。下载完毕后,新闻保存图像
    	downloader.newsItem = item;
    
    	//传入下载完毕后的回调函数
    	[downloader setCompletionHandler:^{
    
    		//下载完毕后要运行的回调部分,block的实现
    		//依据indexPath获取cell对象,并载入图像
    #pragma mark cellForRowAtIndexPath-->没看到过
    		NewsListCell * cell = (NewsListCell *)[self.tableView cellForRowAtIndexPath:indexPath];
    		cell.picImageView.image = downloader.newsItem.newsPic;
    
    	}];
    
    	//開始下载
    	[downloader startDownloadImage:item.newsPicUrl];
    
    	[downloader release];
    }
    
    
    - (void)loadImagesForOnscreenRows
    {
    #pragma mark indexPathsForVisibleRows-->没看到过
    	//获取tableview正在window上显示的cell,载入这些cell上图像。通过indexPath能够获取该行上须要展示的cell对象
    	NSArray * visibleCells = [self.tableView indexPathsForVisibleRows];
    	for (NSIndexPath * indexPath in visibleCells) {
    		NewsItem * item = [_dataArray objectAtIndex:indexPath.row];
    		if (item.newsPic == nil) {
    			//假设新闻还没有下载图像。開始下载
    			[self startPicDownload:item forIndexPath:indexPath];
    		}
    	}
    }
    
    #pragma mark - 延迟载入关键
    //tableView停止拖拽,停止滚动
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
    	//假设tableview停止滚动。開始载入图像
    	if (!decelerate) {
    
    		[self loadImagesForOnscreenRows];
    	}
         NSLog(@"%s__%d__|%d",__FUNCTION__,__LINE__,decelerate);
    }
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
    	//假设tableview停止滚动。開始载入图像
    	[self loadImagesForOnscreenRows];
    
    }
    

  • 相关阅读:
    看完这篇,网络面试稳了!
    Python 单元测试详解
    聊一聊,Python自动化测试框架
    测试妹纸说,你这用了几年的postman,只用了它的皮毛
    Win系统设置Apache Tomcat开机后台自动启动
    .Net Framework中的委托与事件——热水器事例
    Unity3d学习清单
    python 进制、ASCII码转换
    python正则 re 模块函数
    mysql 简单手工注入
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10819224.html
Copyright © 2020-2023  润新知