利用NSOperation进行异步图片下载——设置UITabView数据,图片下载,占位图。解决异步下载和Cell重用造成的图片设置混乱。和多次重复下载的问题
来源:互联网 发布:怎样搭建服务器网络 编辑:程序博客网 时间:2024/05/18 18:46
同步下载图片:同步下载图片。就是直接下载,然后设置。因为下载是一个耗时操作,同步执行又限制当前操作执行不完就不允许执行下一个操作。从而造成界面卡顿的现象。如下图所示:
为了解决这个问题:需要使用异步下载:即当前操作不执行完毕可以执行下一个操作。如下图所示:
占位图:UITabViewCell中UIImageView是懒加载的方式执行的,如果没有图片是默认不显示的。所以一加载完毕是没有图片显示的图片的frame为0 。点击Cell会触发Cell的layoutSubView操作,才会给Cell重新布局。layoutSubView是UIView的方法,根据组件的大小进行设置,如果是图片则根据图片的实际大小进行布局。这时在图片下载完成之前就要使用占位图。
异步下载:异步下载图片不可以直接给Cell设置图片,因为Cell是重用的,重用后会重新赋值造成图片数据混乱。最好的解决方法是利用MVC为Model设置下载后的数据进行保存。也就是说给Cell赋值不可以跳过Model直接赋值。
利用MVC解决方案如下:
利用MVC通过Model为Cell设置数据,避免了直接为Cell赋值由于Cell的重用造成的数据混乱的问题。
另外加了一个判断,如果Model有数据直接为Cell赋值,没有数据的情况下才进行异步任务下载图片,下载完成后刷新当前Cell。从而解决了重用多次重复下载的问题。
特别注意:刷新Cell,意味着重新初始化Cell。重新从头开始运行,再执行一次判断,Model是否有图片数据,如果有的话就直接设置。
新建工程,操作步骤如下:
2
3
4
5
6
代码如下:
新建Model如下:
CZApp.h
//// CZApp.h// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import <UIKit/UIKit.h>@interface CZApp : NSObject@property(nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *icon;@property (nonatomic, strong) NSString *download;/** 保存网络下载的图像 */@property(nonatomic, strong) UIImage *image;+(instancetype) appWithDict:(NSDictionary *) dict;@endCZApp.m
//// CZApp.m// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import "CZApp.h"@implementation CZApp+(instancetype) appWithDict:(NSDictionary *) dict{ CZApp *app = [[self alloc] init]; [app setValuesForKeysWithDictionary:dict]; return app;}@end
控制器代码如下:
//// ViewController.m// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import "ViewController.h"#import "CZApp.h"@interface ViewController ()// plist文件数据的容器@property (nonatomic, strong) NSArray *appList;// 管理下载的全局队列@property (nonatomic, strong) NSOperationQueue *opQueue;@end@implementation ViewController// 懒加载-(NSArray *)appList{ if (_appList == nil) { NSArray *dicArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; // 字典转模型 NSMutableArray *arryM = [NSMutableArray array]; for(NSDictionary *dict in dicArray){ CZApp *app = [CZApp appWithDict:dict]; [arryM addObject:app]; } _appList = arryM; } return _appList;}-(NSOperationQueue *)opQueue{ if (_opQueue == nil) { _opQueue = [[NSOperationQueue alloc] init]; } return _opQueue;}#pragma mark - 实现数据源方法-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.appList.count;}/** 问题1:如果网速比较慢,会很卡 解决方法:使用异步下载 问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示 解决办法:使用占位图(如果展位图比较大, 自定义cell可以解决) 问题3:单元格重用,下拉后造成图片设置错乱 */// cell里面的imageView子控件是懒加载-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"AppCell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 给Cell设置数据 CZApp *app = self.appList[indexPath.row]; cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; // 判断模型里面是否有图像 if (app.image) { NSLog(@" 图片尚未下载......"); cell.imageView.image = app.image; }else{ // 如果模型内没有图片就异步下载 // 显示图片 cell.imageView.image = [UIImage imageNamed:@"user_default"]; // 异步下载图片 NSBlockOperation *downLoadOp = [NSBlockOperation blockOperationWithBlock:^{ // 模拟延时 [NSThread sleepForTimeInterval:0.5]; NSLog(@"正在下载中......"); // 1. 下载图片(二进制数据) NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; // 2. 将下载的数据保存到模型 app.image = [UIImage imageWithData:data]; // 3. 在主线程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // cell.imageView.image = [UIImage imageWithData:data]; // 因为Cell的图片是懒加载的方式添加的。只设置了Image但是没有设置Frame,只有调用layoutSubView()方法才会给Cell子控件重新布局 // layoutSubView是UIView的方法 // 注意:点击Cell也会触发layoutSubView()方法 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; /** reload 会重新调用cell的初始化方法, 会重新判断模型里面是否有图像 有的话就直接显示 */ }]; }]; // 将操作添加到队列 [self.opQueue addOperation:downLoadOp]; } return cell;}@end
控制器代码重构后如下所示:
//// ViewController.m// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import "ViewController.h"#import "CZApp.h"@interface ViewController ()// plist文件数据的容器@property (nonatomic, strong) NSArray *appList;// 管理下载的全局队列@property (nonatomic, strong) NSOperationQueue *opQueue;/**所有下载的缓冲池*/@property (nonatomic, strong) NSMutableDictionary *operationCache;@end@implementation ViewController// 懒加载-(NSArray *)appList{ if (_appList == nil) { NSArray *dicArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; // 字典转模型 NSMutableArray *arryM = [NSMutableArray array]; for(NSDictionary *dict in dicArray){ CZApp *app = [CZApp appWithDict:dict]; [arryM addObject:app]; } _appList = arryM; } return _appList;}-(NSOperationQueue *)opQueue{ if (_opQueue == nil) { _opQueue = [[NSOperationQueue alloc] init]; } return _opQueue;}-(NSMutableDictionary *)operationCache{ if (_operationCache == nil) { _operationCache = [[NSMutableDictionary alloc] init]; } return _operationCache;}#pragma mark - 实现数据源方法-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.appList.count;}/** 问题1:如果网速比较慢,会很卡 解决方法:使用异步下载 问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示 解决办法:使用占位图(如果展位图比较大, 自定义cell可以解决) 问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为Cell的重用导致图片混乱 解决方法:MVC,使用Model(模型)保存下载的图像,再次刷新表格。 问题4:在用户快速滚动的时候,会重复添加下载任务到下载队列。 解决方法:建立下载操作的缓冲池。首先检查缓冲池里是否有当前图片的下载操作。有的话就不创建下载操作。从而保证一张图片只添加一个下载操作。其实就是建立一个全局的字典属性。 *//** 代码重构:1.如果代码太长。 目的: - 写的时候,如果思路清楚,能够一次性写完,但是也要注意同构。 - 时间长了,不好阅读 - 重构代码,便于维护 重构方法:如果有一部分代码专门解决某一问题,就封装起来。 1. 新建一个方法—> 剪切代码。 2. 传参数。 3. 在原来剪切代码的地方,调用抽取的方法。 4. 注意,测试。 5. 注意if嵌套,在实际的开发,非常忌讳很深的嵌套。 */// cell里面的imageView子控件是懒加载-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"AppCell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 给Cell设置数据 CZApp *app = self.appList[indexPath.row]; cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; // 判断模型里面是否有图像 if (app.image) { NSLog(@" 图片已经下载......"); cell.imageView.image = app.image; }else{ // 如果模型内没有图片就异步下载 // 显示图片—占位图 cell.imageView.image = [UIImage imageNamed:@"user_default"]; #warning 从这里开始剪切的代码 // 下载图片 [self downloadImage:indexPath]; } return cell;}-(void)downloadImage:(NSIndexPath *)indexPath{ CZApp *app = self.appList[indexPath.row]; /** 如果下载缓冲池里面有当前图片的下载操作,就不用创建下载操作,没有才创建 缓冲池字典中 key:存放当前图片的url,字符串类型。 Value:保存下载操作 */ if (self.operationCache[app.icon]) { NSLog(@"正在玩命下载中......"); return; } // 缓冲池没有下载操作 // 异步下载图片 NSBlockOperation *downLoadOp = [NSBlockOperation blockOperationWithBlock:^{ // 模拟延时 [NSThread sleepForTimeInterval:2]; NSLog(@"正在下载中......"); // 1. 下载图片(二进制数据) NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; // 2. 将下载的数据保存到模型 app.image = [UIImage imageWithData:data]; // 3. 在主线程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // cell.imageView.image = [UIImage imageWithData:data]; // 因为Cell的图片是懒加载的方式添加的。只设置了Image但是没有设置Frame,只有调用layoutSubView()方法才会给Cell子控件重新布局 // layoutSubView是UIView的方法 // 注意:点击Cell也会触发layoutSubView()方法 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; /** reload 会重新调用cell的初始化方法, 会重新判断模型里面是否有图像 有的话就直接显示 */ }]; }]; // 将操作添加到队列 [self.opQueue addOperation:downLoadOp]; NSLog(@"操作的数量------------->%ld", self.opQueue.operationCount); // 将操作添加到缓冲池中(使用图片的url作为key) [self.operationCache setObject:downLoadOp forKey:app.icon];}@end
- 利用NSOperation进行异步图片下载——设置UITabView数据,图片下载,占位图。解决异步下载和Cell重用造成的图片设置混乱。和多次重复下载的问题
- NSOperation之为操作缓冲池解决操作重复添加操作的问题——UItabView的Cell数据下载并刷新的案例
- KVO和异步图片下载的结合使用
- 异步下载图片,动态设置cell高度
- iOS编程------UITableView中图片的异步下载 / KVO监测Model图片下载
- IOS学习 NSOperation 网络下载图片 涉及plist文件、重用Cell、占位图、缓存池、清理内存
- NSOperation之 设置UITabView的有沙盒图片缓存——剖析内在原理
- 异步图片下载问题随笔
- 图片下载-同步/异步(delegate和block)
- 多线程开发之NSOperation&NSOperationQueue——异步下载图片
- 多线程开发NSOperation&NSOperationQueue——异步下载图片
- 比较简洁的异步图片下载方法
- AsyncTask --异步任务+简单的网络图片下载
- NSOPeration 之UItabView无沙盒缓存——UITabView下载图片后 从操作缓冲池清除下载操作
- 图片下载和json下载(sd卡)
- convertView重用和异步加载图片造成错乱问题解决
- 多线程和异步下载图片
- 利用XMLHttpRequest同步和异步下载二进制文件的解决方案。
- 异步JavaScript的进化
- java
- git基础
- usort php shell
- JVM
- 利用NSOperation进行异步图片下载——设置UITabView数据,图片下载,占位图。解决异步下载和Cell重用造成的图片设置混乱。和多次重复下载的问题
- (新手)请问怎么实现单击BUTTON后使原来textview的内容发生改变
- Java接口使用问题
- Eclipse框架下的组件
- URL编码以及GET和POST提交乱码解决方案
- Windows2008 R2上完全卸载Oracle操作步骤
- 功率MOS管并联方法的研究
- java utils
- 两分钟学会在GitHub托管代码