Robert
Robert

Reputation: 38223

Speed up UIImagePickerController by using a low res image then later swapping in the high res image

There is a great wiki about image loading from the camera picker. Which made me aware of costs of taking an image at full resolution.

At the moment, when a photo is picked, I push a new view controller and display the image at full resolution. Pushing the view is a really slow and choppy experience (about 1 fps!) that I want to smooth out. Comparing to picking a photo on Instagram, I notice that they use a low resolution image and later swap in the full image. (I need the full res image because the user should be able to zoom and pan)

The idea I want is somthing like this:

- (void)imagePickerController:(UIImagePickerController *)picker
        didFinishPickingMediaWithInfo:(NSDictionary *)info 
{

    UIImage* fullImage = [info objectForKey:UIImagePickerControllerOriginalImage];

    // Push a view controller and give it the image.....
}

- (void) viewDidLoad {

    CGSize smallerImageSize = _imageView.bounds;
    UIImage* smallerImage = [MyHelper quickAndDirtyImageResize:_fullImage     
                                                        toSize:smallerImageSize];

    // Set the low res image for now... then later swap in the high res
    _imageView.image = smallerImage;

    // Swap in high res image async
    // This is the part im unsure about... Im sure UIKit isn't thread-safe!
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, NULL), ^{
        _imageView.image = _fullImage;
    });
}

I think that UIImage isn't memory mapped in until it is used. Therefore it dons't slow things down until its given to the imageView. Is this correct?

I think image decoding is already done asynchronously by the system, however, it is sill slowing the phone down considerably while its loading.

Is there a way do perform some of the work required to display an image in a very low priority background queue?

Upvotes: 1

Views: 3939

Answers (3)

khose
khose

Reputation: 763

Have you tried using ALAssetsLibrary to load the thumbnail of that image instead of trying to load the image at full resolution? It's faster than resize it as well.

Upvotes: 1

Rikkles
Rikkles

Reputation: 3372

In addition to Andrey's answer, instead of using imageScaledToSize, use CGImageSourceCreateThumbnailAtIndex. In fact, it's very possible (I am pretty sure it's the case) that any image used in the photo album already has a thumbnail. So instead of bothering with the image itself, grab the existing thumbnail and display it, then use Andrey's code to switch in the main image. This way you do as little work as possible during the animation period.

Calling CGImageSourceCreateThumbnailAtIndex will return the thumbnail image, whether it's already there or needs to be generated. So it'll be quite safe to use, and probably at least as fast as imageScaledToSize.

You can find complete code samples to use it in the Apple docs, no need to duplicate it here.

Upvotes: 3

Andrei Chevozerov
Andrei Chevozerov

Reputation: 1029

You're trying to do things the most complicated way :) Why not just prepare the small image before pushing the view controller and pass it to them? Look at this code:

- (void)imagePickerController:(UIImagePickerController *)picker
        didFinishPickingMediaWithInfo:(NSDictionary *)info 
{

    UIImage *fullImage = [info objectForKey:UIImagePickerControllerOriginalImage];
    UIImage *smallImage = [fullImage imageScaledToSize:self.view.bounds];

    // Push a view controller and give it BOTH images
}

// And in your pushed view controller

- (void)viewDidLoad
{
    _imageView.image = self.smallImage;
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    _imageView.image = self.fullImage;
}

The main thing is that viewDidAppear: will be called right after the animation is done so you can switch images here without any worries.

Upvotes: 5

Related Questions