Determine largest PHAsset image size available on iOS device

IosPhotokitPhassetIcloud Photo-Library

Ios Problem Overview


How can I determine the largest PHAsset image size that is available on an iOS device, where by "available" I mean currently on the device, not requiring any network download from iCloud Photo Library?

I've been developing an app where a collection of thumbnails are shown in a UICollectionView (much like the Photos app) and then a user can select an image to see it full size. The full size image is downloaded from iCloud if necessary. However, I want to show the user the largest possible version of the image until the full size is available.

The way the Photos Framework works is rather odd:

In the collection view, I'm using a PHCachingImageManager to cache (and later request) images with a targetSize of 186x186 pixels (double the UICollectionViewCell dimensions because of retina scaling).

That works very well, with very fast scrolling and no lag in filling in the cells with images. Checking the size of the images returned, they are all roughly 342x256 pixels. That's on my iPhone 6 with iCloud Photo Library enabled and 'Optimize Storage' enabled, so only small thumbnails are stored on device for most images.

However, the odd part is that if I then request the full size (or much larger) version of any image, either with PHCachingImageManager or PHImageManager.defaultManager() using the PHImageRequestOptionsDeliveryMode.Opportunistic option, the first (low quality) image that is returned is a tiny 60x45 pixel postage stamp that looks horrible on the full screen, until eventually the full size image is downloaded to replace it.

Clearly there are larger thumbnails (342x256) on the device already, as they were just shown in the collection view! So why the hell doesn't it return them as the first 'low quality' image until the full size has been downloaded?? Just to make sure the 342x256 images really were already on the device I ran the code in airplane mode so no network access was happening.

What I found was that if I requested any size up to 256x256 I would get the 342x256 images returned. As soon as I went larger (even 1 pixel) it would first return me 60x45 images and then try to download the full size image.

I've changed my code to make a 256x256 request first, followed by a full size request with PHImageRequestOptionsDeliveryMode.HighQualityFormat (which doesn't return a tiny intermediate image), and that works beautifully. It's also what the iOS Photos app must be doing as it displays relatively high quality thumbnails whilst it's downloading the full image.

So, with that background, how do I know what the largest on device image size is for a PHAsset? Is 256x256 a magic number, or does it depend on how iCloud Photo Library is optimising for space (based on size of library and available device storage)?

Certainly seems like a bug that Photos Framework is not returning the largest image currently available (when asked for a size larger than it currently has), and that to get the largest size that it has, you have to ask for that size or smaller, because if you ask for larger, you'll get a 60x45 miniature! Bizarre.

UPDATE: Bug report submitted to Apple: https://openradar.appspot.com/25181601
Example project that demonstrates the bug: https://github.com/mluisbrown/PhotoKitBug

Ios Solutions


Solution 1 - Ios

Using the example project posted in the question I was able to reproduce the issue in the simulator, but when I made the following change I would receive only the largest image:

let targetSize = PHImageManagerMaximumSize

Solution 2 - Ios

Actually I don't know the reason why Apple would return the degraded 4560 image with PHImageResultIsDegradedKey=1. For me, I just request the 256256 image with deliveryMode PHImageRequestOptionsDeliveryModeHighQualityFormat at first, then I use the other PHImageRequestOptions to request full size image like below:

PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.networkAccessAllowed = YES;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info){
// code to update the iCloud download progress
});

[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
// code to update UIImageView or something else.
});

And I think, Instagram may be use this similar solution to handle the image from iCloud.

Solution 3 - Ios

[imageManager requestImageForAsset:asset
              targetSize: PHImageManagerMaximumSize
              contentMode:PHImageContentModeAspectFill
              options:nil
resultHandler:^(UIImage *result, NSDictionary *info)
{
    result;
}];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmluisbrownView Question on Stackoverflow
Solution 1 - IosmarkwatsonatxView Answer on Stackoverflow
Solution 2 - IosChris ForeverView Answer on Stackoverflow
Solution 3 - Iosvikarn baraiView Answer on Stackoverflow