iOS開發實踐之cell下載圖片(NSOperation)
滾動列表cell的圖片從伺服器上下載顯示,利用多線程和緩衝技術 高效下載顯示圖片。
cell下載圖片思路:
1、定義images字典存放下載後的圖片(圖片下載url作為key,圖片作為value)cell圖片先去images字典中找,沒有就往下(沙箱中尋找)。
2、尋找沙箱是否存在,若存在就設定cell圖片,否則顯示佔位圖片(增強體驗感)並開啟線程下載圖片。
3、定義字典operations存放所有的下載操作(url是key,operation對象是value)。判斷下載操作是否存在,若存在 說明下載中,否則建立下載操作。
4、下載完成後,更新主線程:將圖片添加存放圖片的images字典中,將操作從operations字典中移除(防止operations越來越大,保證下載失敗後,能重新下載),將圖片儲存到沙箱中,並重新整理表格。
案例:應用管理介面cell
1、應用程式模型
App.h
#import @interface App : NSObject//應用程式名稱@property(nonatomic,copy) NSString *name;//下載量@property(nonatomic,copy) NSString *download;//表徵圖地址@property(nonatomic,copy) NSString *icon;+(instancetype)appWithDict:(NSDictionary *)dict;@end
App.m
#import "App.h"@implementation App+(instancetype)appWithDict:(NSDictionary *)dict{ App *app = [[App alloc]init]; [app setValuesForKeysWithDictionary:dict]; return app;}@end
2、定義隊列、存放操作字典、存放圖片字典、應用app變數
//應用app@property(nonatomic,strong) NSMutableArray *apps;//存放所有下載圖片的隊列@property(nonatomic,strong) NSOperationQueue *queue;//存放所有的下載操作(url是key,operation對象是value)@property(nonatomic,strong) NSMutableDictionary *operations;//存放所有下載完的圖片@property(nonatomic,strong) NSMutableDictionary *images;#pragma 懶載入-(NSMutableArray *)apps{ if (_apps==nil) { NSMutableArray *appArr = [NSMutableArray array]; //取出plist檔案轉換字典 NSString *file = [[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:file]; //字典轉模型 for (NSDictionary *dict in dictArr) { App *app = [App appWithDict:dict]; [appArr addObject:app]; } _apps = appArr; } return _apps;}-(NSOperationQueue *)queue{ if (!_queue) { self.queue = [[NSOperationQueue alloc]init]; } return _queue;}-(NSMutableDictionary *)operations{ if (!_operations) { self.operations = [[NSMutableDictionary alloc]init]; } return _operations;}-(NSMutableDictionary *)images{ if (_images) { self.images = [[NSMutableDictionary alloc]init]; } return _images;}
3、設定cell,線程下載圖片
#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"app"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } //取出模型 App *app = self.apps[indexPath.row]; //設定cell cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; // 先從images緩衝中取出圖片url對應的UIImage UIImage *image = self.images[app.icon]; if (image) {// 說明圖片已經下載成功過(成功緩衝) cell.imageView.image = image; }else{// 說明圖片並未下載成功過(並未緩衝過) // 獲得caches的路徑, 拼接檔案路徑 NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:[app.icon lastPathComponent]]; // 先從沙箱中取出圖片 NSData *data = [NSData dataWithContentsOfFile:file]; if (data) {// 沙箱中存在這個檔案 cell.imageView.image = [UIImage imageWithData:data]; }else{// 沙箱中不存在這個檔案 //顯示佔位圖片 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; // 下載圖片 [self download:app.icon indexPath:indexPath]; } } return cell;}-(void)download:(NSString *)imageUrl indexPath:(NSIndexPath *)indexPath{ //取出當前圖片url對應下的下載操作(operations對象) NSBlockOperation *operation = self.operations[imageUrl]; if (operation) return; __weak typeof(self) appsVC = self; operation = [NSBlockOperation blockOperationWithBlock:^{ NSURL *url = [NSURL URLWithString:imageUrl]; NSData *data = [NSData dataWithContentsOfURL:url];//下載圖片 UIImage *image = [UIImage imageWithData:data];//轉化為image //回到住線程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (image) { //存放到字典中 appsVC.images[imageUrl] = image; //圖片存到沙箱中解) //UIImage --> NSData --> File(檔案) NSData *data = UIImagePNGRepresentation(image); NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:[imageUrl lastPathComponent]]; NSLog(@"%@",file); [data writeToFile:file atomically:YES]; } // 從字典中移除下載操作 (防止operations越來越大,保證下載失敗後,能重新下載) [appsVC.operations removeObjectForKey:imageUrl]; // 重新整理表格 [appsVC.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; }]; // 添加操作到隊列中 [self.queue addOperation:operation]; // 添加到字典中 (這句代碼為瞭解決重複下載) self.operations[imageUrl] = operation;}
4、表格拖拽時停止下載,停止拖拽時開始下載
/** * 當使用者開始拖拽表格時調用 */-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ //暫停下載 [self.queue setSuspended:YES];}/** * 當使用者停止拖拽表格時調用 */-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ //恢複下載 [self.queue setSuspended:NO];}
5、記憶體警告,移除所有緩衝字典。
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // 移除所有的下載操作緩衝 [self.queue cancelAllOperations]; [self.operations removeAllObjects]; // 移除所有的圖片緩衝 [self.images removeAllObjects];}
效果: