UITableViewCell load images and reused cells

i need to load from web/files some UIImages. I was searching and i found in other 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 scroll fast to bottom or top the images are loaded wrong and there is a short lag when it ends.

  • How to manage session for a user logged in from mobile app in PHP?
  • iOS 7: UITableView shows under status bar
  • Method returning value from asynchronous block with FacebookSDK
  • CocoaPod/Podspec and *.framework
  • OpenXML spreadsheet created in .NET won't open in iPad
  • core data ios9: multifield unique constraint
  • So the question is.. how can i load the UIImages in the correct cell when i use queues to fetch them? Thanks!

    2 Solutions Collect From Internet About “UITableViewCell load images and reused cells”

    I suspect the incorrect images you see are a result of you not setting your place holder image in the event of you having a local copy of an image but still retrieving the local copy asynchronously. Also In the code you appended for loading the local copy you use UIImage a UIKit component on a background thread.

    Also interestingly you seem to be doing some kind of UIImage caching. Adding the images to what I assume is an NSMutableArray property named imageFriends. But you seem to have commented out the cache add in the event you have a local copy of the file. Also your posted code never uses the cached UIImages.

    While 2 levels of caching seems a bit overboard if you wanted to do this you could 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];
                   }
               }); 
            }
        });
    }
    

    UIImages are part of UIKit and not thread safe. But you can load the NSData on another thread.

    You are loading image asynchronously, so during fast scrolling cells get reused faster then images are downloaded. One way to avoid loading a wrong image, would be to check if cell already got reused when image is loaded. Or cancel all requests in progress when you dequeue new cells.

    I would also recommend to look at AFNetworking, as it contains helpful 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 cancelImageRequestOperation method, to cancel requests in progress. 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"]];