This is a common problem. The big problem is that by the time the cell image appeared, the cell might have been recycled and can be used for some other element. If you try to set the image for the cell, the image will finally be available, there is a good chance that you will set the image for the wrong item.
So, do not try to update the cell directly. Do not even save the link to the cell in the image upload completion block. Instead, the completion block updates the data model, regardless of what it looks like. You can also keep a weak reference to the collection view and save the cell index path. When the image arrives, the completion code may call -reloadItemsAtIndexPaths: which will cause the collection to reload the cell for the affected index path if the cell is still visible. Your method is -collectionView:cellForItemAtIndexPath: just does its usual thing, but this time the image will be available in the data model.
So your code will look something like this:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { static NSString * CellIdentifier = @"Event"; EventCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; Event *event = [events objectAtIndex:indexPath.item]; // replace "Event" with whatever class you use for your items cell.eventTitle.text = [event objectForKey:@"title"]; cell.eventImage.image = [event objectForKey:@"image"]; if (cell.eventImage.image == nil) { NSString *imageUrl = [[[events objectAtIndex:indexPath.item] objectForKey:@"photo"] objectForKey:@"url"]; dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetched", NULL); dispatch_async(imageFetchQ, ^{ __weak UICollectionView *weakCollection; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; UIImage *image = [UIImage imageWithData:imageData]; if (image) { dispatch_async(dispatch_get_main_queue(), ^{ [event setObject:image forKey:@"image"]; // updating the model here [weakCollection reloadItemsAtIndexPaths:@[indexPath]]; }); } }); } return cell; }
I have not tested this exact code, because I donβt know what your data model looks like. Remember to replace your own data element class for the Event class that I suggested. However, I use the same technique in my own code, and I think this is the right way. Basic moments:
- Updating the image in your data model means that it will be available the next time the item is displayed, even if the item is not displayed when loading the image.
- Using a weak collection view link prevents problems if images are still arriving when the user moves from the collection view.
- Calling
-reloadItemsAtIndexPaths: helps limit the changes to the cell to your method ...cellForItemAtIndexPath:
One caveat is that if the list of things you show can change (for example, if the user can add or remove items over time), then it may not even be safe for your completion code to rely on the index path the same . In this case, you will need to determine the index path for the element when the image appears.
source share