開始在cell中是使用這個函數來加載圖片的。
[self.photoView sd_setImageWithURL:[NSURL URLWithString: [post objectForKey: @"thumb_url"]] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {...}
非常簡潔的API,好舒服。但是在tableview第一次加載數據的時候,屏幕第二個cell的圖片總是無法顯示,通過打日志發現,第二個cell的圖片的請求並沒回調completed接口,后續很多cell的圖片請求都沒有回調,被取消了。看了一下源碼,才知道,這個API的請求會取消掉之前產生圖片下載請求任務。比如我們reloadData,加載了20個CELL的數據,產生了20個圖片下載請求,但是實際上可能真正執行的也就是第一個和最后一個,中間的下載任務由於是隊列中的任務,還沒有真正發出去就被取消掉了。
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { [self sd_cancelCurrentImageLoad];
解決的辦法,就是使用稍微復雜一點的API
@interface TFeedCell() @property (nonatomic) id<SDWebImageOperation> photoDownloadOperation; @end - (void)setPost: (NSDictionary *)post shoudDownloadImage: (BOOL)shouldDownload { _post = post; //其他UI元素設置。。。 self.photoView.image = nil; //由於我使用了FDTemplateCell開源項目,setPost方法會在tableView delegate的 cellForRow和HeightForRow都被調用,在heightForRow調用setPost不需要去做圖片下載,會產生較多的重復下載,所以增加了這個參數。 if (shouldDownload) { if (self.photoDownloadOperation) { [self.photoDownloadOperation cancel]; } self.photoDownloadOperation = nil; WS(ws); self.photoDownloadOperation = [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString: [post objectForKey: @"thumb_url"]] options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (!error) { ws.photoView.image = image; } else { DDLogDebug(@"%@", error); } }]; } }
downloadImageWithURL這個方法不會取消之前產生的任務。但是我們在加載20個cell的時候並不希望20個圖片都下載,所以需要自己管理下載任務,需要時才下載。
因為cell是重用的,雖然reloadData加載了20個數據(假設一個網絡請求20條數據),但實際上產生的cell不過幾個,取決於屏幕可見大小,看不見的部分理論上有十幾個cell實際上都是重用了同一個cell。利用這個機制,只要cell被加載的時候,我們就把當前cell關聯的圖片下載請求取消掉,產生新的圖片下載請求,那么實際上真正有效的圖片下載請求只有屏幕可見的幾個cell和最后一個看不見的cell。從用戶使用的角度感覺就是邊滾動邊下載。