0
votes

I have a UICollectionView displaying a grid of custom cells. When I setup the cell in cellForItemAtIndexPath I call a method on the cell subclass to setup the UI (e.g. [cell setupUI]). This method does some loading of labels but also calls an async method which fetches images from the web. I'm using AFNetworking to handle this.

My issue is that sometimes (quite often) the images get loaded in the correct cells + cells that shouldn't have an image set. The data just seems to get loaded in random cells.

I've been looking at this for a while and tried multiple approaches but I can't figure out where I'm going wrong. Is there a gotcha I'm missing?

NB: I've checked and the method that sets these image cells is called the correct number of times (3) but images appear on 5 cells.

Edit

This is the code used to fetch the images. I've previously used this without CollectionView (drawing the grid myself) and it worked fine:

  [self.youtubeThumbnail setImageWithURLRequest:request placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
        weakImageView.image = image;
 } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){

 }];

weakImageView is a weak reference to my cells image view. The above code is in the cell subclass and is called from cellForItemAtIndexPath.

I should point out this method is run on dispatch_async(dispatch_get_global_queue) and the image loading is on dispatch_async(dispatch_get_main_queue).

Solution

So my problem was actually quite strange. My collection view was being reloaded multiple times and this caused the data to appear multiple times in incorrect cells. I'm still not 100% sure why this is the case but I have managed to hack around it by checking if the content has been reloaded, setting a BOOL, and preventing a reload again. The answer I have selected is correct helped my get the async loading correct which was also likely part of the issue. Thanks for your help.

2
Could you post the code where you're fetching the images? It could be as simple as a reloadData in the wrong place or a fetching issue. Additionally, in order to troubleshoot and exclude wrong assumptions, try to downloading the image Array at app launch thus to store it locally. - carlodurso
@carlodurso I added the code which gets the image and some more explanation. - user470763
I should point out this method is run on dispatch_async(dispatch_get_global_queue) and the image loading is on dispatch_async(dispatch_get_main_queue). - user470763

2 Answers

0
votes

The gotcha is probably related to cell reuse (without providing code though, I'm just guessing). As you're scrolling through a collection view the cells shown on screen are recycled, and later reused to avoid the overhead of having to create a ton of objects when scrolling. There's more info in the UICollectionView class reference.

More specifically the problem you're seeing is likely that by the time your network response is coming down, the cell that initially created the request is offscreen. You can fix this by following Apple's old LazyTableImages sample app. The key is in the implementation of startIconDownload:forIndexPath: in the RootViewController.

[iconDownloader setCompletionHandler:^{

        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

        // Display the newly loaded image
        cell.imageView.image = appRecord.appIcon;

        // Remove the IconDownloader from the in progress list.
        // This will result in it being deallocated.
        [self.imageDownloadsInProgress removeObjectForKey:indexPath];

    }];

So when the image finishes downloading, the table view vends the correct cell to the completion block which sets the imageView. So, you will have the correct Table View Cell or (or in your case, Collection View Cell). And, if the cell fires off a request but is then scrolled offscreen before it comes down, there's nothing you have to worry about because the TableView/CollectionView method will return nil for offscreen cells.

0
votes

If you're willing to use any third party libraries, I recommend you take a look at SDWebImage. It solves the issue of having the incorrect cells having the incorrect data when asynchronously downloading content.

It's code is similar to this:

[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

It extends UIImageView with simple methods to easily have your collections have and keep the right image. This also works for standard UITableViewCells.