首先先了解一下我們多圖片下載的一般解決方案
注:以下模擬AppStore瀏覽購買項目場景
主體思路
場景還原:
進入app,當圖片還沒顯示完成時,我們不停的拖拽tableview,就會不停的調用 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. 導致以下倆個問題.
出現(xiàn)問題:
- 重復下載操作
- cell重用導致圖片錯位
第一次進入app我們會進行下載圖片,由于下載圖片是一個耗時操作,此時用戶進行拖拽,由于cell的重用機制移出屏幕的cell會被重用,之前的cell會被重用到下方,恰巧圖片又剛好下載完成,這時候如果我們任然進行顯示
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = image;
}];
就會出現(xiàn) --- 植物大戰(zhàn)僵尸2 捕魚 都出現(xiàn)了錯亂
解決辦法:
1.為了防止圖片下載操作重復,我們將對應的操作對象做記錄,無論是成功還是失敗最后我們都移除該對象,保證操作的唯一性.
/** 內存緩存的圖片 */
@property (nonatomic, strong) NSMutableDictionary *images;
/** 所有的操作對象 */
@property (nonatomic, strong) NSMutableDictionary *operations;
2.下載完成我們去刷新對應的cell
// 回到主線程顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
整體實現(xiàn)代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 先從內存緩存中取出圖片
UIImage *image = self.images[app.icon];
if (image) { // 內存中有圖片,顯示圖片
cell.imageView.image = image;
} else { // 內存中沒有圖片,檢查沙盒中是否有圖片
// 獲得Library/Caches文件夾
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 獲得文件名
NSString *filename = [app.icon lastPathComponent];
// 計算出文件的全路徑
NSString *file = [cachesPath stringByAppendingPathComponent:filename];
// 加載沙盒的文件數(shù)據(jù)
NSData *data = [NSData dataWithContentsOfFile:file];
if (data) { // 沙盒中有圖片,直接利用沙盒中圖片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
// 存到字典中
self.images[app.icon] = image;
} else { // 沙盒中沒有圖片,下載圖片
cell.imageView.image = [UIImage imageNamed:@"placeholder"];
//查詢是否有下載任務
NSOperation *operation = self.operations[app.icon];
if (operation == nil) { // 這張圖片暫時沒有下載任務
operation = [NSBlockOperation blockOperationWithBlock:^{
// 下載圖片
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
// 數(shù)據(jù)加載失敗
if (data == nil) {
// 移除操作
[self.operations removeObjectForKey:app.icon];
return;
}
UIImage *image = [UIImage imageWithData:data];
// 存到字典中
self.images[app.icon] = image;
// 回到主線程顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
// 將圖片文件數(shù)據(jù)寫入沙盒中
[data writeToFile:file atomically:YES];
// 移除操作
[self.operations removeObjectForKey:app.icon];
}];
// 添加到隊列中
[self.queue addOperation:operation];
// 存放到字典中
self.operations[app.icon] = operation;
}
}
}
return cell;
}
小結:由于自己去完成多圖片的下載 細節(jié)比較多,需要考慮cell重用錯亂,重復下任務,網絡錯誤data為空等等一系列的細節(jié)問題,為了提高開發(fā)效率我們通常會使用第三方框架
最終解決方案
第三方框架 SDWebImage
- 導入頭文件
#import "UIImageView+WebCache.h"
- 加載圖片設置占位圖
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"]];
對照
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
//只需要一句話
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"]];
return cell;
}
而且它也幫我們完成了緩存的處理
讓我們簡單的了解一下如何使用這個強大的第三方框架
1.下面我們以參數(shù)較多的方法舉例:
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// expectedSize: 圖片的總字節(jié)數(shù)
// receivedSize: 已經接收的圖片字節(jié)數(shù)
NSLog(@"下載進度:%f", (double)receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
NSLog(@"下載完圖片");
}];
1社牲,sd_setImageWithURL獲取網絡圖片
2悴了,placeholderImage占位圖片
3熟空,progress 下載進度 用法: NSLog(@"下載進步:%f",(double)receivedSize / expectedSize);
4, *image *error *imageURL分別完成后返回 的圖片巡揍,錯誤和下載地址
5腮敌,SDImageCacheType cacheType 是枚舉類型糜工,圖片存儲位置在內存捌木、磁盤或無
6,SDWebImageOptions 枚舉類型
用法:SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageLowPriority
SDWebImageRetryFailed 下載失敗重復下載 常用
SDWebImageLowPriority 當UI交互的時候暫停下載 常用
SDWebImageCacheMemoryOnly 只存圖片在內存
SDWebImageProgressiveDownload 可以像瀏覽器那樣從上往下下載刷新圖片
SDWebImageRefreshCached 刷新緩存
SDWebImageHighPriority 高優(yōu)先級
SDWebImageDelayPlaceholder 不加載占位圖
options參數(shù)圖片
2.內存處理
因為SDWebImgae是屬于整個項目彬檀,不是屬于某個控制器窍帝,所以不要在控制器里的didReceiveMemoryWarning處理內存問題坤学,而且在AppDelegate.m添加applicationDidReceiveMemoryWarning方法
- AppDelegate中 (√)
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
// 取消所有下載
[[SDWebImageManager sharedManager] cancelAll];
// 清除內存緩存
[[SDWebImageManager sharedManager].imageCache clearMemory];
}
- 當前控制器中 出現(xiàn)內存警告(×)
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
self.images = nil;
self.operations = nil;
[self.queue cancelAllOperations];
}
3.其他功能
- SDWebImage的圖片緩存周期是多長:1個星期
//設置100天深浮,默認是7天
[SDWebImageManager sharedManager].imageCache.maxCacheAge = 100 * 24 * 60 * 60
- SDWebImage的圖片最大尺寸(字節(jié))
//無默認值飞苇,單位字節(jié)
[SDWebImageManager sharedManager].imageCache.maxCacheSize = ;
拓展:只下載圖片不設置 --- 給Button設置圖片時可以使用
[[SDWebImageManager sharedManager] downloadImageWithURL:<#(NSURL *)#> options:<#(SDWebImageOptions)#> progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
}]