2012-12-27 141 views
2

我正在顯示一個表格。每行都有一個圖像圖標,從URL加載。Objective C - UITableViewCell異步加載圖像

由於下載圖像同步阻止用戶界面,我已經通過宏中央調度實現了異步方式。

我的問題是,當我向下滾動時,由於單元格被重新使用,不正確的圖像顯示出來。

我可以猜出爲什麼會發生這種情況 - 這是因爲重新使用的單元格更新圖像,因此,以前的單元格現在將具有新下載的和錯誤的圖像。什麼是解決這個問題的理想方法?

這是我的代碼。

對於下載的每個圖像,我將它存儲在一個名爲「ImageStore」的單例類中。

// set the data for each cell - reusing the cell 
- (UITableViewCell *)tableView:(UITableView *)tableView 
     cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; 

    if (!cell) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
             reuseIdentifier:@"UITableViewCell"]; 
    } 

    // setting the image for each cell 
    // first check if there is UIImage in the ImageStore already 
    NSString *imageUrl = [obj objectForKey:@"image"]; 
    if (imageUrl) { 
     if ([[ImageStore sharedStore] imageForKey:imageUrl]) { 

     [[[tableView cellForRowAtIndexPath:indexPath] imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]]; 
     } else { 

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
    dispatch_async(queue, ^{ 
     UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: 
     [NSURL URLWithString:[obj objectForKey:@"image"]]]]; 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
     [[ImageStore sharedStore]setImage:image forKey:imageUrl]; 
     [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationNone]; 

      [[[tableView cellForRowAtIndexPath:indexPath] imageView] setImage:image]; 
     }); 
    }); 
     } 
    } 
    return cell; 
} 

回答

4

試試這個:

NSString *imageUrl = [obj objectForKey:@"image"]; 
if (imageUrl) { 
    if ([[ImageStore sharedStore] imageForKey:imageUrl]) { 
    [[cell imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]]; 
    } else { 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
    dispatch_async(queue, ^{ 
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: 
    [NSURL URLWithString:[obj objectForKey:@"image"]]]]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
    [[cell imageView] setImage:image]; 
    [cell setNeedsLayout]; 
    }); 
    }); 
} 

編輯:選中此grand-central-dispatch-for-ios-lazy-loading的實現代碼如下懶加載

0

我已經注意到了這種行爲,太。

我能找到的唯一通用解決方案是禁用單元重用。

2

從修改王子一點點的回答

NSString *imageUrl = [obj objectForKey:@"image"]; 

if (imageUrl) 
{ 
    if ([[ImageStore sharedStore] imageForKey:imageUrl]) 
    { 
     //This condition means the current cell's image has been already downloaded and stored. So set the image to imageview 
     [[cell imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]]; 
    } 
    else 
    { 
     //While reusing this imageView will have previous image that will be visible till the image is downloaded. So i am setting this image as nil. 
     [[cell imageView] setImage:nil]; 

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
     dispatch_async(queue, ^{ 

     //Called Immediately. 
     UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: 
     [NSURL URLWithString:[obj objectForKey:@"image"]]]]; 
     dispatch_sync(dispatch_get_main_queue(), ^{ 

      //Called when the image is downloaded 
      //Store in any external object. So that next time reuse this will not be downloaded 
      [[ImageStore sharedStore]setImage:image forKey:imageUrl]; 
      //Also set the image to the cell 
      [[cell imageView] setImage:image]; 
      [cell setNeedsLayout]; 
     }); 
    }); 
    } 
} 
2

AFNetworking看看......他們做出處理網絡回調EASY!我建議AFNetworking高於ASIHTTPRequest,因爲AFNetworking保持更新,ASIHTTPRequest不是......他們剛剛停止開發。

下面是如何使用AFNetworking異步下載圖片的例子:

NSDictionary * object = [self.array objectAtIndex:indexPath.row]; 
NSDictionary * image = [object valueForKey:@"image"]; 
NSString *imageUrl = [image valueForKeyPath:@"url"]; 
NSURL *url = [NSURL URLWithString:imageUrl]; 
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"empty-profile-150x150.png"]]; 

setImageWithURL: placeholderImage:方法是我用來做這個......而不是多個方法和代碼行,我做到的一切用這條線。

這整行代碼完全符合你想要做的。真的沒有必要重新創造輪子:)。它確實有幫助,但是要通過更低層次的編程來真正理解真正在底層進行的實現。

查看鏈接以下載庫並查看更多關於如何使用它的示例......認真地說,它讓您的生活變得更加輕鬆,無需擔心線程和GCD的問題。

AFNetworking Demo

我還沒有潛入的AFNetworking的代碼,但我的應用程序加載圖像進入細胞時運行像黃油。它看起來不錯:)

哦,這裏的文檔爲AFNetworkingAFNetworking Documentation