UITableViewCell loadable images and reusable cells

I need to download some UIImages from web files. I searched and found in another question this code:

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]; }); }); } 

But the problem is that when I quickly scroll down or up, the images load incorrectly and there is a short lag when it ends.

So the question is, how can I load UIImages in the correct cell when I use queues to retrieve them? Thanks!

+6
ios uitableview
source share
2 answers

I suspect that the incorrect images that you see are the result of not setting the image of your place owner in case you have a local copy of the image, but still extract the local copy asynchronously. Also in the code added to download the local copy, you use the UIImage a UIKit in the background thread.

It is also interesting that you are doing some kind of caching of UIImage . Adding images to what I am assuming is an NSMutableArray property called imageFriends . But you seem to have commented on adding a cache in case you have a local copy of the file. Also your published code never uses cached UIImages .

While the 2 levels of caching seem a bit overpriced, if you want to do this, you can do something like this:

 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]; } }); } }); } 

UIImage are part of UIKit and are not thread safe. But you can load NSData into another stream.

+7
source share

You load the image asynchronously, so during fast scrolling, the cells are reused again, and then the images are loaded. One way to avoid loading the wrong image would be to check if the cell is really reused when loading the image. Or cancel all running queries when you deactivate new cells.

I also recommend watching AFNetworking , as it contains a useful category for UIImageView , so you can do something like this

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

It also contains the cancelImageRequestOperation method to cancel running queries. Then your code will look like this:

 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"]]; 
+6
source share

All Articles