程式碼範例
代碼可以到這裡下載,傳送門demo
原文連結:http://www.qingpingshan.com/rjbc/ios/214222.html
iOS開發中,UITableView的最佳化一直是一個老生常談的問題,除了常用的預先載入,緩衝等方法以外,其實利用RunLoop 的迴圈也可以實現超清大圖的流暢載入,具體的使用方法我們利用一個demo來詳細理解
首先,我們有一個需求,就是要從網路載入高清大圖到UITableViewCell上,而且每個Cell上面載入多張圖片,當cell數量過多的時候,我們需要保持流暢度和載入速度。
那麼我們做一個簡單的分析:
1,因為這裡用到了Runloop迴圈,那麼我們可以監聽到runloop的每次迴圈,在每一次迴圈當中我們考慮去進行一次圖片下載和布局。2,既然要在每次迴圈執行一次任務,我們可以先把所有圖片載入的任務代碼塊添加到一個數組當中,每次迴圈取出第一個任務進行執行。*3,因為runloop在閑置的時候會自動休眠,所以我們要想辦法讓runloop始終處於迴圈中的狀態。
好的,那麼我們就可以開始考慮代碼實現: 第一步,先把uitableview基本效果實現
@property (strong,nonatomic) UITableView* showImageTableView;//懶載入-(UITableView *)showImageTableView{ if (!_showImageTableView) { _showImageTableView = [[UITableView alloc]initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain]; _showImageTableView.backgroundColor = [UIColor yellowColor]; _showImageTableView.delegate = self; _showImageTableView.dataSource = self; } return _showImageTableView;}//註冊 [self.showImageTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ShowImageTableViewReusableIdentifier];//添加 [self.view addSubview:self.showImageTableView];//資料來源代理#pragma mark- UITableViewDelegate-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ShowImageTableViewReusableIdentifier]; //每個cell中添加3張圖片 for (int i = 0; i < 3; i++) { #這裡是添加圖片的方法 } return cell;}-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 399;}-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 135;}
第二步,初始化可變數組用來儲存任務(代碼塊)
typedef void(^SaveFuncBlock)();//存放任務的數組@property (nonatomic, strong) NSMutableArray *saveTaskMarr;//最大任務數(超過最大任務數的任務就停止執行)@property (nonatomic, assign) NSInteger maxTasksNumber;//任務執行的代碼塊@property (nonatomic, copy) SaveFuncBlock saveFuncBlock;-(NSMutableArray *)saveTaskMarr{ if (!_saveTaskMarr) { _saveTaskMarr = [NSMutableArray array]; } return _saveTaskMarr;} self.maxTasksNumber = 18;
第三步,建立cell添加圖片的方法
-(void)addImageToCell:(UITableViewCell*)cell andTag:(NSInteger)tag{ UIImageView* cellImageView = [[UIImageView alloc]initWithFrame:CGRectMake(tag*(ImageWidth+5), 5, ImageWidth, ImageHeight)]; dispatch_async(dispatch_get_global_queue(0,0), ^{ NSData* imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img5.duitang.com/uploads/item/201312/14/20131214173346_iVKdT.jpeg"]]; dispatch_async(dispatch_get_main_queue(), ^{ cellImageView.image = [UIImage imageWithData:imageData]; [cell.contentView addSubview:cellImageView]; }); });}
第四步,將任務添加到數組儲存
//新增工作進數組儲存-(void)addTasks:(SaveFuncBlock)taskBlock{ [self.saveTaskMarr addObject:taskBlock]; //超過每次最多執行的任務數就移出當前數組 if (self.saveTaskMarr.count > self.maxTasksNumber) { [self.saveTaskMarr removeObjectAtIndex:0]; }}
第五步,在cellForRow方法當中,添加方法:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ShowImageTableViewReusableIdentifier]; for (int i = 0; i < 3; i++) { //新增工作到數組 __weak typeof(self) weakSelf = self; [self addTasks:^{ //下載圖片的任務 [weakSelf addImageToCell:cell andTag:i]; }]; } return cell;}
第六步,監聽RunLoop 第七步,也是最關鍵的步驟,使用定時器,保持RunLoop迴圈中。
//定時器,保證runloop一直處於迴圈中@property (nonatomic, weak) NSTimer *timer;self.timer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(setRunLoop) userInfo:nil repeats:YES];//此方法主要是利用計時器事件保持runloop處於迴圈中,不用做任何處理-(void)setRunLoop{}
最後一步,在runLoop迴圈中去處理事件
//MARK: 回呼函數//定義一個回呼函數 一次RunLoop來一次static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ ViewController * vcSelf = (__bridge ViewController *)(info); if (vcSelf.saveTaskMarr.count > 0) { //擷取一次數組裡面的任務並執行 SaveFuncBlock funcBlock = vcSelf.saveTaskMarr.firstObject; funcBlock(); [vcSelf.saveTaskMarr removeObjectAtIndex:0]; }}
好啦。到此為止,我們想要的效果就基本出來了,大家可以去試試:hushed:,同樣的道理也可以應用於其他情境。