0

When I take a PHAssetCollection and give it to PHAsset.fetchAssetsInAssetCollection:options I am given back a collection of PHAssets. For every UIImage given back to me, there is a nil value given back as well.

The following is my cellForItemAtIndexPath:indexPath method.

public override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AssetCellIdentifier", forIndexPath: indexPath) as? AssetCollectionViewCell;

    let asset = self.assetFetchResult!.objectAtIndex(indexPath.row);
    self.imageManager.requestImageForAsset(
        asset as! PHAsset,
        targetSize: CGSize.init(width: asset.pixelWidth, height: asset.pixelHeight),
        contentMode: PHImageContentMode.AspectFill,
        options: nil) { (image, info) -> Void in
            print (image);
            if (image == nil) {
                return;
            }


            cell?.assetThumbnail.contentMode = UIViewContentMode.ScaleAspectFit;
            cell?.assetThumbnail.image = image;
    }

    return cell!;
}

If I don't put the nil check

if (image == nil) {
    return;
}

Then the UICollectionView just renders nothing. As soon as I add the nil check, the collection view starts rendering the images from the photo library's asset collection.

Why are there always the same number of nils given back to me as there are UIImage objects? Is there something in my request that I am messing up?

This is the output of my print invocation above.

Optional(<UIImage: 0x12f8263d0>, {40, 30})
Optional(<UIImage: 0x12e5265a0>, {40, 30})
Optional(<UIImage: 0x12e664c90>, {40, 30})
Optional(<UIImage: 0x12e74c860>, {40, 30})
Optional(<UIImage: 0x12e58c580>, {40, 30})
Optional(<UIImage: 0x12e60b6f0>, {40, 30})
Optional(<UIImage: 0x12e7657d0>, {40, 30})
Optional(<UIImage: 0x12e6655e0>, {40, 30})
Optional(<UIImage: 0x12e743ca0>, {40, 30})
Optional(<UIImage: 0x12e5fb6e0>, {40, 30})
Optional(<UIImage: 0x12e5fb8e0>, {40, 30})
Optional(<UIImage: 0x12f85f3b0>, {40, 30})
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil

Update:

It seems that this happens across every PHAssetCollection that I am given back.

Update 2:

This is the bits from the WWDC example I am trying to replicate. The one thing I did notice that it does, that I am not, is tagging the cells in order to prevent overriding it if it's being reused.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    AAPLGridViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath];

    // Increment the cell's tag
    NSInteger currentTag = cell.tag + 1;
    cell.tag = currentTag;

    PHAsset *asset = self.assetsFetchResults[indexPath.item];
    [self.imageManager requestImageForAsset:asset
                                 targetSize:AssetGridThumbnailSize
                                contentMode:PHImageContentModeAspectFill
                                    options:nil
                              resultHandler:^(UIImage *result, NSDictionary *info) {

        // Only update the thumbnail if the cell tag hasn't changed. Otherwise, the cell has been re-used.
        if (cell.tag == currentTag) {
            cell.thumbnailImage = result;
        }

    }];

    return cell;
}
Johnathon Sullinger
  • 7,097
  • 5
  • 37
  • 102

1 Answers1

0

You cannot call self.imageManager.requestImageForAsset in the way you are calling it in the middle of cellForItemAtIndexPath. This method, cellForItemAtIndexPath, returns immediately. Meanwhile, requestImageForAsset is asynchronous - meaning that your completion handler is called later. By the time it is called, we've moved on to a different cell; and remember, cells are reused. So your entire approach is nonsensical.

Take a look at Apple's example code to see how to populate a collection view's cells using the caching version of the image manager.

Also, I notice that you are ignoring the PHImageRequestOptions. It's very important. You should be thinking carefully about what you want here. If you set no value, this means that you run the risk that your completion handler will be called more than once, which again makes no sense when we are no longer in the process of populating this cell.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I will look at that. My understanding from watching the WWDC session was that it was handled, developers didn't have to worry about it, unless they wanted to filter what was given. If no options, then everything was returned. I also thought it was weird that it was 50/50. For every image, there was a corresponding nil. If it cloud only photos were the cause, I wouldn't think each album would have exactly 50% unavailable. – Johnathon Sullinger Oct 08 '15 at 02:44
  • How would you recommend it? I am more or less just replicating the code Apple shipped in their WWDC example. I wouldn't think I'd want to grab every image in the asset collection, that seems like a huge use of bandwith and memory if the user never scrolls. I know i can manage it in batches; the PHImageManager is supposed to handle that for me though listening to Apple's talk on it. So I'm a bit confused on what better way to take :/ – Johnathon Sullinger Oct 08 '15 at 02:49
  • I do get what you're driving at. The cell could be reused before my callback from `imageManager` even finishes. – Johnathon Sullinger Oct 08 '15 at 02:50
  • 1
    I'm not "driving at" that. I'm yelling it! :) – matt Oct 08 '15 at 02:50
  • So is your image manager already a caching image manager? Because in the Apple example it is. – matt Oct 08 '15 at 02:55
  • I updated my post showing the source from Apple's example that I am using as my reference source. They are tagging their cells in order to prevent overwriting a reused cell. Is They are also not passing in any options. I'll read up again on the options, when I read through the docs last time, it sounded more like just a way to filter what you get back. Leaving it nil gave back everything which is what i wanted in this example. – Johnathon Sullinger Oct 08 '15 at 02:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/91683/discussion-between-johnathon-sullinger-and-matt). – Johnathon Sullinger Oct 08 '15 at 02:56