UICollectionView - Downloading images and effective reloading of data, like Instagram

afnetworking, cocoa-touch, ios, objective-c, uicollectionview

Solution

The technique you want to implement is called lazy loading. Since you are using `AFNetworking` it will be easier to implement this in your case. Each of your collection view cell needs to have a `UIImageView` to display the image. Use the `UIImageView+AFNetworking.h` category and set the correct image URL by calling method

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // ....

    [cell.imageView setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

    // ...
    return cell;
}

Placeholder is the image which will be displayed until required image is downloaded. This will simply do the required job for you.

Note: By default, URL requests have a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`.

Also, for you reference, if you want to implement lazy loading of images yourself, follow this Apple sample code. This is for `UITableView` but same technique can be used for `UICollectionView` as well.

Hope that helps!

Problem

I noticed that apps like Intagram uses `UICollectionViews` to display the feed of photos. I also noticed that the cells for these photos is somehow placed on screen before the actual photos are downloaded completely. Then when a download completes, that photo is nicely displayed in the correct cell. I would like to copy that functionality, but I do not know how to go about it. I am currently downloading a large JSON object which I transform to an array of NSDictionaries. Each `NSDictionary` contains information about each photo, and among that information, an URL is presented. At this URL, I can find the corresponding image that I need to download and display in my `UICollectionViewCells` As for now, I iterate this list and initiate a download for each URL I see. When that download is complete, I reload the collectionview using `[self.collectionView reloadData]`. But, as you can imagine, if I have 30 cells that all wants an image, there is a lot of `reloadData` calls. I am using AFNetworking to handle the download, here is the method, which I call based on the URL I mentioned before: ``` -(void) downloadFeedImages:(NSString *) photoURL imageDescs:(NSDictionary*)imageDescs photoId:(NSString *)photoID{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *directory = [paths objectAtIndex:0]; NSString* foofile = [directory stringByAppendingPathComponent:photoID]; if([[NSFileManager defaultManager] fileExistsAtPath:foofile]){ // IF IMAGE IS CACHED [self.collectionView reloadData]; return; } NSLog(@"photoURL: %@", photoURL); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:photoURL]]; AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { // Save Image NSLog(@"URL-RESPONSE:%@", image); NSString *myFile = [directory stringByAppendingPathComponent:photoID]; NSData *imageData = UIImagePNGRepresentation(image); [imageData writeToFile:myFile atomically: YES]; [self.collectionView reloadData]; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"ERROR: %@", [error localizedDescription]); }]; [[WebAPI sharedInstance] enqueueHTTPRequestOperation:operation]; } ``` So basically, I wonder how I can achieve the functionality that Instagram and similar applications has when it comes to displaying a feed with images. In addition, I would like to know a good way to initiate a download for each cell, and when that download is finished, update that cell, not redraw the entire view using [reloadData] Thanks

Original source