How to get requested image size for iOS8 PhotoKit?

5.1k views Asked by At

I am using the code below for fetching an image:

[[PHImageManager defaultManager] requestImageForAsset:asset 
targetSize:CGSizeMake(800, 600) 
contentMode:PHImageContentModeAspectFill 
options:options 
resultHandler:^(UIImage *result, NSDictionary *info) {
    NSLog(@"size:%@",NSStringFromCGSize(result.size));                
}];

I'm requesting an image size of 800 x 600, but I'm getting an image size of 45 x 60, which is very poor-quality.

How can i get the requested image size using PhotoKit?

2

There are 2 answers

2
Unnati On BEST ANSWER

I was able to get image of size 800*600 using below code:

PHImageRequestOptions* options = [[[PHImageRequestOptions alloc] init] autorelease];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode =  PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeExact;

[PHImageManager defaultManager]
requestImageForAsset:(PHAsset *)asset
targetSize:CGSizeMake(800, 600)
contentMode:PHImageContentModeAspectFit
options:options
resultHandler:^(UIImage *result, NSDictionary *info) {
   NSLog(@"Image size:%@",NSStringFromCGSize(result.size));
}];

And if you want to get full size image you can use PHImageManagerMaximumSize as targetSize.

1
Josh Heald On

Your (Unnati's) self answer will work, however it misses out on a number of the optimisations which Apple give you in their framework which get images for you do display more quickly.

Your original code was pretty much correct, but you include an options object which you probably don't need, and may have been causing you problems if you had set the resizeMode incorrectly on it. Here is the code which should work for you, I've just removed your options object.

[[PHImageManager defaultManager]
 requestImageForAsset:(PHAsset *)asset
 targetSize:CGSizeMake(800, 600)
 contentMode:PHImageContentModeAspectFit
 options:nil
 resultHandler:^(UIImage *result, NSDictionary *info) {
     NSLog(@"Image size:%@",NSStringFromCGSize(result.size));
 }];

This should be simpler, and give you all the optimisations Apple provide with their framework for free. Here's the explanation of what you get:

Responding quickly with a low quality image

The framework will call your result handler multiple times with increasing quality images, allowing you to display something to the user while they wait for the final image. From the documentation:

Photos may call your result handler block more than once. Photos first calls the block to provide a low-quality image suitable for displaying temporarily while it prepares a high-quality image. (If low-quality image data is immediately available, the first call may occur before the method returns.) When the high-quality image is ready, Photos calls your result handler again to provide it.

This means your resultHandler block needs to be safe to call more than once - the block in your code is as it just logs the size, and for my test image I get two log messages, first one with a low quality 40x26 px image, and then again with a high quality image. If you open the iOS Photos app, you can see low quality images very briefly before they appear to sharpen - this is what's happening here.

Using options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat prevents this behaviour, and your resultHandler will only be called once with the final image, so I would suggest not setting this option unless you can't devise a handler block (in your real code) which can be called multiple times.

Returning a cached high quality image quickly

To provide the final image quickly, the framework will provide an image close to the requested size in the final call to your resultHandler block, if it has one cached already. If you're using a UIImageView set with a contentMode of aspect fill or fit, this won't be a problem, just specify the matching contentMode in your image request. For example, against my test image, the second call to my resultHandler is with a 1280x850 image, larger than what I requested but the right ballpark. However, setting options.resizeMode = PHImageRequestOptionsResizeModeExact will prevent this behaviour and force the framework to resize a fresh copy of the image rather than using a cached copy, which takes time.

Since the Photos framework doesn't crop images (even if you ask it to, but that's a bug ;-)), you won't necessarily get the exact size you request, even using the PHImageRequestOptionsResizeModeExact option. For example, if you request 800x600 for an image which is not in a 4:3 ratio, Photos can't return an image of that size, it just gets as close as it can - on my latest image, a request like that gets an image of 800x531. This means that you need to be able to handle images which aren't the exact size that you request, so you might as well take advantage of the optimisations in the framework and avoid using PHImageRequestOptionsResizeModeExact. From the documentation again:

Resizing to exactly match a target size is less efficient than using the fast resizing option.

(Did you have this set to PHImageRequestOptionsResizeModeFast in your original options object, which was not included in the question? That may have been causing your issue.)

Image versions

It's possible to request different versions of an image, as you do in your self answer with options.version = PHImageRequestOptionsVersionCurrent. This one is not required however, as the default is the current version.