2012-09-07 148 views
15

我正在嘗試加載Flickr照片(cs193p iOS Stanford,作業5)的UITableView列表。爲了避免UI阻塞事件,我推遲了每個單元格的縮略圖下載到不同的隊列中(但是將UI更新回主隊列中)。此代碼不會異步加載圖像,但只要點擊UITableViewCell行就會添加縮略圖。 (見下面的截圖)。任何想法我做錯了什麼?異步UITableViewCell使用GCD加載圖像

PS:我已經看過一些其他的stackoverflow問題& Apple的LazyTableImages示例,但我仍然相信這是達到理想效果的最簡潔的方法。

謝謝!

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Photo List Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell 
    NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; 


    if (photo != nil) { 
     if ([[photo objectForKey:@"title"] length] > 0) { 
      cell.textLabel.text = [photo objectForKey:@"title"]; 
     } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { 
      cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; 
     } else { 
      cell.textLabel.text = @"Unknown"; 
     } 
    } 
    cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; 

    // Fetch using GCD 
    dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); 
    dispatch_async(downloadThumbnailQueue, ^{ 
     UIImage *image = [self getTopPlacePhotoThumbnail:photo]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      if ([self.tableView.visibleCells containsObject:cell]) { 
       [cell.imageView setImage:image]; 
      } 
     }); 
    }); 
    dispatch_release(downloadThumbnailQueue); 

    return cell; 
} 

之前點擊一行 Before clicking a row

選擇行 After selecting the row

更新後:對於那些有興趣,這是我最後使用的代碼:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Photo List Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell 
    NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; 

    if (photo != nil) { 
    if ([[photo objectForKey:@"title"] length] > 0) { 
     cell.textLabel.text = [photo objectForKey:@"title"]; 
    } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { 
     cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; 
    } else { 
     cell.textLabel.text = @"Unknown"; 
    } 
    } 
    cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; 

    // Fetch using GCD 
    dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); 
    dispatch_async(downloadThumbnailQueue, ^{ 
    UIImage *image = [self getTopPlacePhotoThumbnail:photo]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     UITableViewCell *cellToUpdate = [self.tableView cellForRowAtIndexPath:indexPath]; // create a copy of the cell to avoid keeping a strong pointer to "cell" since that one may have been reused by the time the block is ready to update it. 
     if (cellToUpdate != nil) { 
      [cellToUpdate.imageView setImage:image]; 
      [cellToUpdate setNeedsLayout]; 
     } 
    }); 
    }); 
    dispatch_release(downloadThumbnailQueue); 

    return cell; 
} 
+0

嘿,這完全是題外話,但不知道是否該塊內發送到主線程,而不是檢查對象上的平等'cell',檢查'indexPath'處的單元格是否存在? –

+0

我剛試過這個:if([self.tableView cellForRowAtIndexPath:indexPath]!= nil){... etc},它的工作原理也很好。這是你想到的嗎? – sybohy

+0

沒錯,不是直接在你的塊中使用'cell.imageView',而是在'indexPath'處抓取單元格並使用它的圖像視圖。我認爲如果直接在主線程塊內使用'cell'的方式,可能會遇到單元複用的問題。 –

回答

15

您應該致電設置縮略圖後,在單元格上顯示3210。

從Apple文件:當你要調整視圖的子視圖的佈局

「叫上你的應用程序的主線程這種方法這種方法使得記請求,並立即返回由於這種方法確實。不強制立即更新,而是等待下一個更新週期,在更新任何視圖之前,可以使用它來使多個視圖的佈局無效。此行爲允許您將所有佈局更新合併到一個更新週期,這通常對性能更好。「

+0

哇。那很快!感謝卡爾,我從來沒有想過我自己。再次感謝!! – sybohy

+0

不客氣,很高興它的工作! –

+0

其實,快速的問題:在技術上不是更好的調用[細胞setNeedsLayout]?我正在閱讀Apple的文檔layoutSubviews,這就是我發現的:「你不應該直接調用這個方法[layoutSubviews]。如果你想強制佈局更新,改爲調用setNeedsLayout方法, 「。你怎麼看? (在功能上都是這樣) – sybohy

0

另一個解決方案(以及我用於該賦值的解決方案)是在將UIImage分配給該imageView後,在該單元的imageView上調用setNeedsDisplay。所以基本上,我創建了一個名爲FlickrDownloader的類,它包裝了FlickrFetcher(以及我的緩存代碼)的功能。 FlickrDownloader有這個方法:

(void)getImageForPhotoInfo:(NSDictionary *)photoInfo ofFormat:(FlickrPhotoFormat)format withCallback:(image_setter_callback_t)callback; 

這基本上是FlickrFetcher的相同功能的調用,但它增加了一個回調,多數民衆贊成在下載照片完成後調用。在更新一個UITableViewCell的ImageView的情況下的回調看起來是這樣的:

image_setter_callback_t setImage = ^(UIImage *image){ 
    cell.imageView.image = image; 
    [cell.imageView setNeedsDisplay]; 
}; 
+0

是否解決了將幀設置爲'(0,0,0,0)'的單元?當我嘗試類似的東西時,它不會調整大小,直到我強制調用'layoutSubviews'。你是否使用佔位符圖像? –

+0

如果你調用[cell.imageView setNeedsDisplay],那不需要你的類來實現drawRect方法嗎?回顧2011年秋季講座4幻燈片#8,setNeedsDisplay設置所有內容以調用drawRect。它並沒有真正說明它是否多於或少於這個數字。可以? – sybohy

+0

@CarlVeazey你是對的,我使用了LazyTableImages示例中的佔位符圖像。由於這是我從一開始的方法,也許這就是我設置框架的方法。 –