iOS 8 Load Images Fast From PHAsset

Aggressor picture Aggressor · Dec 10, 2014 · Viewed 11.8k times · Source

I have an app that lets people combine up to 4 pictures. However when I let them choose from their photos (up to 4) it can be very slow even when I set image quality to FastFormat. It will take 4 seconds (about 1 second per photo). On highest quality, 4 images takes 6 seconds.

Can you suggest anyway I get get the images out faster?

Here is the block where I process images.

func processImages()
    {
        _selectediImages = Array()
        _cacheImageComplete = 0
        for asset in _selectedAssets
        {
            var options:PHImageRequestOptions = PHImageRequestOptions()
            options.synchronous = true
            options.deliveryMode = PHImageRequestOptionsDeliveryMode.FastFormat
            PHImageManager.defaultManager().requestImageForAsset(asset, targetSize:CGSizeMake(CGFloat(asset.pixelWidth), CGFloat(asset.pixelHeight)), contentMode: .AspectFit, options: options)
                {
                    result, info in
                    var minRatio:CGFloat = 1
                    //Reduce file size so take 1/3 the screen w&h
                    if(CGFloat(asset.pixelWidth) > UIScreen.mainScreen().bounds.width/2 || CGFloat(asset.pixelHeight) > UIScreen.mainScreen().bounds.height/2)
                    {
                        minRatio = min((UIScreen.mainScreen().bounds.width/2)/(CGFloat(asset.pixelWidth)), ((UIScreen.mainScreen().bounds.height/2)/CGFloat(asset.pixelHeight)))
                    }
                    var size:CGSize = CGSizeMake((CGFloat(asset.pixelWidth)*minRatio),(CGFloat(asset.pixelHeight)*minRatio))
                    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
                    result.drawInRect(CGRectMake(0, 0, size.width, size.height))
                    var final = UIGraphicsGetImageFromCurrentImageContext()
                    var image = iImage(uiimage: final)
                    self._selectediImages.append(image)
                    self._cacheImageComplete!++
                    println(self._cacheImageComplete)
                    if(self._cacheImageComplete == self._selectionCount)
                    {
                        self._processingImages = false
                        self.selectionCallback(self._selectediImages)
                    }
            }

        }
    }

Answer

rickster picture rickster · Dec 10, 2014

Don't resize the images yourself — part of what PHImageManager is for is to do that for you. (It also caches the thumbnail images so that you can get them more quickly next time, and shares that cache across apps so that you don't end up with half a dozen apps creating half a dozen separate 500MB thumbnail caches of your whole library.)

func processImages() {
    _selectediImages = Array()
    _cacheImageComplete = 0
    for asset in _selectedAssets {
        let options = PHImageRequestOptions()
        options.deliveryMode = .FastFormat

        // request images no bigger than 1/3 the screen width
        let maxDimension = UIScreen.mainScreen().bounds.width / 3 * UIScreen.mainScreen().scale
        let size = CGSize(width: maxDimension, height: maxDimension)

        PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: size, contentMode: .AspectFill, options: options)
            { result, info in
                // probably some of this code is unnecessary, too,
                // but I'm not sure what you're doing here so leaving it alone
                self._selectediImages.append(result)
                self._cacheImageComplete!++
                println(self._cacheImageComplete)
                if self._cacheImageComplete == self._selectionCount {
                    self._processingImages = false
                    self.selectionCallback(self._selectediImages)
                }
            }
        }
    }
}

Notable changes:

  • Don't ask for images synchronously on the main thread. Just don't.
  • Pass a square maximum size to requestImageForAsset and use the AspectFill mode. This will get you an image that crops to fill that square no matter what its aspect ratio is.
  • You're asking for images by their pixel size here, and the screen size is in points. Multiply by the screen scale or your images will be pixelated. (Then again, you're asking for FastFormat, so you might get blurry images anyway.)