The thing you want to be aware of when mixing asynch and tables is that the asynch finishes at an unknown time in the future, possibly after the cell is scrolled away, removed, reused, etc.
Also, the image that gets pulled from the web is lost if that cell is scrolled away. Not sure if AFNetworking caches for you, but it might be better to not assume. Here's a solution using native networking:
// ...
NSDictionary *post = [posts objectAtIndex:indexPath.row];
NSString *postpictureUrl = [post objectForKey:@"picture"];
// find a place in your model, or add one, to cache an actual downloaded image
UIImage *postImage = [post objectForKey:@"picture_image"];
if (postImage) {
cell.imageView.image = postImage; // this is the best scenario: cached image
} else {
// notice how we don't pass the cell - we don't trust its value past this turn of the run loop
[self asynchLoad:postpictureUrl forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:@"default"];
}
// ...
Now, a no-nonsense asynch load without any 3rd party help
- (void)asynchLoad:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath {
NSURL *url = [NSURL urlWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
// create the image
UIImage *image = [UIImage imageWithData:data];
// cache the image
NSDictionary *post = [posts objectAtIndex:indexPath.row];
[post setObject:image forKey:@"picture_image"];
// important part - we make no assumption about the state of the table at this point
// find out if our original index path is visible, then update it, taking
// advantage of the cached image (and a bonus option row animation)
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
if ([visiblePaths containsObject:indexPath]) {
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationFade];
// because we cached the image, cellForRow... will see it and run fast
}
}
}];
}
For this to work, the posts should be created as NSMutableDictionary...
// someplace in your code you add a post to the posts array. do this instead.
NSDictionary *postData = // however you get a new post
[posts addObject:[NSMutableDictionary dictionaryWithDictionary:postData]];
Alternatively, if it's hard to change the posts model directly, you can setup another structure to cache the downloaded images. A mutable dictionary keyed by the url strings is a good structure to use:
@property (nonatomic,strong) NSMutableDictionary *imageCache;
@synthesize imageCache=_imageCache;
// lazy init on the getter...
- (NSMutableDictionary *)imageCache {
if (!_imageCache) {
_imageCache = [NSMutableDictionary dictionary];
}
return _imageCache;
}
Now, when configuring the cell, see if there's a cached image by checking the cache...
// change to the cellForRowAtIndexPath method
NSString *postpictureUrl = [post objectForKey:@"picture"];
UIImage *postImage = [self.imageCache valueForKey:postpictureUrl];
And once an image is downloaded, cache it...
// change to the asynchLoad: method I suggested
UIImage *image = [UIImage imageWithData:data];
[self.imageCache setValue:image forKey:urlString];