2013-04-29 95 views
4

我有一個UICollectionView實現與自定義UICollectionViewCell。 數據的數組從互聯網獲取,但圖像在用戶訪問內容時下載。GCD和細胞重用UICollectionView

雖然這可能不是最有效的方法,我不打算這樣離開,但它確實暴露了我正在努力解決的某個問題。看起來這些單元格顯示「緩存」或「舊」圖像,當我緩慢滾動收集視圖時,單元格圖像不斷變化。

這可能是一個問題,實際的細胞圖像不可從網絡在那個時刻和我的問題是我如何強制一個空單元格或一些加載活性監視器,而不是顯示不正確的圖像?

在此先感謝,小

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
static NSString * CellIdentifier = @"Event"; 

EventCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 


NSString *title = [[events objectAtIndex:indexPath.item] objectForKey:@"title"]; 
NSString *imageUrl = [[[events objectAtIndex:indexPath.item] objectForKey:@"photo"] objectForKey:@"url"]; 


dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetched", NULL); 
dispatch_async(imageFetchQ, ^{ 

    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; 

    if (imageData) 
    { 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      cell.eventImage.image = [UIImage imageWithData:imageData]; 
      cell.eventTitle.text = title; 

     }); 
    } 
}); 


return cell; 

} 
+0

我會建議你使用NSOperationQueue來代替,就像本教程中的內容: http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues – 2013-04-29 08:12:04

+0

我試過按照教程,但它並沒有真正處理重用問題。我看到NSOperationQueue將適用於這種解決方案,但問題依然存在。我想我需要實現某種機制來驗證哪個圖像即將顯示在每個單元格中。不知道如何。然而。 – Pinion 2013-05-02 23:23:43

回答

4

這是一個常見的問題。最大的問題是,當細胞的圖像到達時,細胞可能已被回收並可能用於其他物品。如果您嘗試爲單元格設置圖像,那麼圖像最終可用,因此您很可能會爲錯誤的項目設置圖像。

所以,不要試圖直接更新單元格。甚至不要在圖像下載完成塊中保留對單元格的引用。相反,讓完成塊更新數據模型,無論如何。您也可以保留對集合視圖的弱引用,並存儲單元格的索引路徑。當圖像到達時,完成代碼可以調用-reloadItemsAtIndexPaths:,如果單元仍然可見,這將導致集合視圖爲受影響的索引路徑重新加載單元。你的-collectionView:cellForItemAtIndexPath:方法只是做它通常的事情,但這次圖像將在數據模型中可用。

所以,你的代碼看起來是這樣的:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString * CellIdentifier = @"Event"; 
    EventCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 

    Event *event = [events objectAtIndex:indexPath.item]; // replace "Event" with whatever class you use for your items 
    cell.eventTitle.text = [event objectForKey:@"title"]; 
    cell.eventImage.image = [event objectForKey:@"image"]; 
    if (cell.eventImage.image == nil) { 
     NSString *imageUrl = [[[events objectAtIndex:indexPath.item] objectForKey:@"photo"] objectForKey:@"url"]; 

     dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetched", NULL); 
     dispatch_async(imageFetchQ, ^{ 
      __weak UICollectionView *weakCollection; 
      NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; 
      UIImage *image = [UIImage imageWithData:imageData]; 
      if (image) 
      { 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        [event setObject:image forKey:@"image"]; // updating the model here 
        [weakCollection reloadItemsAtIndexPaths:@[indexPath]]; 
       }); 
      } 
     }); 
    } 
    return cell; 
} 

我沒有測試過這個確切的代碼,因爲我不知道你的數據模型是什麼樣子。請確保將我自己假設的Event類替換爲您自己的數據項類。但是,我在自己的代碼中使用了相同的技術,我認爲這是正確的方法。亮點:

  • 更新數據模型中的圖像意味着,這將是下一個可用的時間的項目需要顯示,即使圖像被下載時,該項目是不可見的。
  • 對集合視圖使用弱引用可以避免在用戶導航離開集合視圖時圖像仍然到達時出現問題。
  • 調用-reloadItemsAtIndexPaths:有助於將對單元格的更改限制爲您的...cellForItemAtIndexPath:方法。

一個需要注意的是,如果你顯示可以更改(例如,如果用戶可以添加或刪除隨着時間的推移項目)事情的清單,然後甚至有可能不會是安全的爲您完成代碼依靠索引路徑保持不變。在這種情況下,您需要在圖像到達時確定項目的索引路徑。