PHAsset to UIImage

SwiftPhasset

Swift Problem Overview


I'm attempting to create a UIImage (like a thumbnail or something) from a PHAsset so that I can pass it into something that takes a UIImage. I've tried adapting solutions I found on SO (since they all just directly pass it into say a tableview or something), but I have no success (likely because I'm not doing it right).

func getAssetThumbnail(asset: PHAsset) -> UIImage {
    var retimage = UIImage()
    println(retimage)
    let manager = PHImageManager.defaultManager()
    manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: nil, resultHandler: {(result, info)->Void in
            retimage = result
    })
    println(retimage)
    return retimage
}

The printlns are telling me that the manager.request line isn't doing anything right now. How do I get it to give me the asset as a UIImage.

Thanks.

Swift Solutions


Solution 1 - Swift

This did what I needed it to do, in case anyone also needs this.

func getAssetThumbnail(asset: PHAsset) -> UIImage {
    let manager = PHImageManager.defaultManager()
    let option = PHImageRequestOptions()
    var thumbnail = UIImage()
    option.synchronous = true
    manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: option, resultHandler: {(result, info)->Void in
            thumbnail = result!
    })
    return thumbnail
}

#Edit: Swift 3 update

func getAssetThumbnail(asset: PHAsset) -> UIImage {
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    var thumbnail = UIImage()
    option.isSynchronous = true
    manager.requestImage(for: asset, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
        thumbnail = result!
    })
    return thumbnail
}

Solution 2 - Swift

try this it works for me, hope it helps you too,

func getUIImage(asset: PHAsset) -> UIImage? {
    
    var img: UIImage?
    let manager = PHImageManager.default()
    let options = PHImageRequestOptions()
    options.version = .original
    options.isSynchronous = true
    manager.requestImageData(for: asset, options: options) { data, _, _, _ in
        
        if let data = data {
            img = UIImage(data: data)
        }
    }
    return img
}

Solution 3 - Swift

Simple Solution (Swift 4.2)

Method 1:

extension PHAsset {
    
    var image : UIImage {
        var thumbnail = UIImage()
        let imageManager = PHCachingImageManager()
        imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { image, _ in
            thumbnail = image!
        })
        return thumbnail
    }
}                          

let image = asset.image 

Use this method if you only need UIImage from PHAsset.

OR

extension PHAsset {
    func image(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) -> UIImage {
        var thumbnail = UIImage()
        let imageManager = PHCachingImageManager()
        imageManager.requestImage(for: self, targetSize: targetSize, contentMode: contentMode, options: options, resultHandler: { image, _ in
            thumbnail = image!
        })
        return thumbnail
    }
}

let image = asset.image(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?)

Use this method for your desired UIImage.

OR

extension PHAsset {
    
    func image(completionHandler: @escaping (UIImage) -> ()){
        var thumbnail = UIImage()
        let imageManager = PHCachingImageManager()
        imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { img, _ in
            thumbnail = img!
        })
        completionHandler(thumbnail)
    }
}

let image = asset.image(completionHandler: {(img) in
    print("Finished")
})

Use this method for notify after completion.

Method 2:

extension PHAsset {
    var data : (UIImage, [AnyHashable : Any]) {
        var img = UIImage(); var information = [AnyHashable : Any](); let imageManager = PHCachingImageManager()
        imageManager.requestImage(for: self, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: nil, resultHandler: { image,info in
            img = image!
            information = info!
        })
        return (img,information)
    }
} 


let image_withData : (UIImage, [AnyHashable : Any]) = asset.data

Use this method if you want UIImage And Result Info of PHAsset

OR

extension PHAsset {
    
    func data(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) -> (UIImage, [AnyHashable : Any]) {
        var img = UIImage(); var information = [AnyHashable : Any](); let imageManager = PHCachingImageManager()
        imageManager.requestImage(for: self, targetSize: targetSize, contentMode: contentMode, options: options, resultHandler: { image,info in
            img = image!
            information = info!
        })
        return (img,information)
    }
}

let data = asset?.data(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?)

Use this method for your desired Data.

Solution 4 - Swift

I'd suggest using Apple's PHCachingImageManager (that inherits from PHImageManager):

> A PHCachingImageManager object fetches or generates image data for photo or video assets

Also, PHCachingImageManager support a better caching mechanism.

Example of fetching a thumbnail synchronous:

let options = PHImageRequestOptions()
options.deliveryMode = .HighQualityFormat
options.synchronous = true // Set it to false for async callback

let imageManager = PHCachingImageManager()
imageManager.requestImageForAsset(YourPHAssetVar,
                                  targetSize: CGSizeMake(CGFloat(160), CGFloat(160)),
                                  contentMode: .AspectFill,
                                  options: options,
                                  resultHandler: { (resultThumbnail : UIImage?, info : [NSObject : AnyObject]?) in
                                                
                                                   // Assign your thumbnail which is the *resultThumbnail*
                                                  }

In addition, you can use PHCachingImageManager to cache your images for faster UI response:

> To use a caching image manager: > > 1. Create a PHCachingImageManager instance. (This step replaces using the > shared PHImageManager instance.) > > 2. Use PHAsset class methods to fetch the assets you’re interested in. > > 3. To prepare images for those assets, call the > startCachingImagesForAssets:targetSize:contentMode:options: method > with the target size, content mode, and options you plan to use when > later requesting images for each individual asset. > > 4. When you need an image for an individual asset, call the > requestImageForAsset:targetSize:contentMode:options:resultHandler: > method, and pass the same parameters you used when preparing that > asset. > > If the image you request is among those already prepared, the > PHCachingImageManager object immediately returns that image. > Otherwise, Photos prepares the image on demand and caches it for later > use.

In our example:

var phAssetArray : [PHAsset] = []
    
for i in 0..<assets.count
{
  phAssetArray.append(assets[i] as! PHAsset)
}
    
let options = PHImageRequestOptions()
options.deliveryMode = .Opportunistic
options.synchronous = false
    
self.imageManager.startCachingImagesForAssets(phAssetArray,
                                              targetSize: CGSizeMake(CGFloat(160), CGFloat(160)),
                                              contentMode: .AspectFill,
                                              options: options)

Solution 5 - Swift

For Swift 3.0.1:

func getAssetThumbnail(asset: PHAsset, size: CGFloat) -> UIImage {
    let retinaScale = UIScreen.main.scale
    let retinaSquare = CGSize(width: size * retinaScale, height: size * retinaScale)//(size * retinaScale, size * retinaScale)
    let cropSizeLength = min(asset.pixelWidth, asset.pixelHeight)
    let square = CGRect(x:0, y: 0,width: CGFloat(cropSizeLength),height: CGFloat(cropSizeLength))
    let cropRect = square.applying(CGAffineTransform(scaleX: 1.0/CGFloat(asset.pixelWidth), y: 1.0/CGFloat(asset.pixelHeight)))

    let manager = PHImageManager.default()
    let options = PHImageRequestOptions()
    var thumbnail = UIImage()

    options.isSynchronous = true
    options.deliveryMode = .highQualityFormat
    options.resizeMode = .exact
    options.normalizedCropRect = cropRect

    manager.requestImage(for: asset, targetSize: retinaSquare, contentMode: .aspectFit, options: options, resultHandler: {(result, info)->Void in
        thumbnail = result!
    })
    return thumbnail
}

Resource : https://gist.github.com/lvterry/f062cf9ae13bca76b0c6#file-getassetthumbnail-swift

Solution 6 - Swift

Swift 4.

resizeMode,deliveryMode - These can be set according to user requirement.

isNetworkAccessAllowed - set this to "true" for fetching images from the cloud

imageSize- required image size

func getImageFromAsset(asset:PHAsset,imageSize:CGSize, callback:@escaping (_ result:UIImage) -> Void) -> Void{
    
    let requestOptions = PHImageRequestOptions()
    requestOptions.resizeMode = PHImageRequestOptionsResizeMode.fast
    requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
    requestOptions.isNetworkAccessAllowed = true
    requestOptions.isSynchronous = true
    PHImageManager.default().requestImage(for: asset, targetSize: imageSize, contentMode: PHImageContentMode.default, options: requestOptions, resultHandler: { (currentImage, info) in
        callback(currentImage!)
    })
}

Solution 7 - Swift

Swift 5

extension PHAsset {
func getAssetThumbnail() -> UIImage {
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    var thumbnail = UIImage()
    option.isSynchronous = true
    manager.requestImage(for: self,
                         targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight),
                         contentMode: .aspectFit,
                         options: option,
                         resultHandler: {(result, info) -> Void in
                            thumbnail = result!
                         })
    return thumbnail
    }
}

Solution 8 - Swift

The problem is that requestImageForAsset is a resultHandler and this block of code happens in the future after your functions has already printed and returned the value you was expecting. I did come changes to show you this happening and also suggest some simple solutions.

func getAssetThumbnail(asset: PHAsset) {
    var retimage = UIImage()
    println(retimage)
    let manager = PHImageManager.defaultManager()
    manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: nil, resultHandler: {
    (result, info)->Void in
            retimage = result
      println("This happens after")
      println(retimage)
      callReturnImage(retimage) // <- create this method
    })
    println("This happens before")
}

Learn more about closures and completion handle and async funcs at Apple documentation

I hope that helps you!

Solution 9 - Swift

Objective-c version of code based on dcheng answer.

-(UIImage *)getAssetThumbnail:(PHAsset * )asset {
   
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
    options.synchronous = true;
    
    __block UIImage *image;
    [PHCachingImageManager.defaultManager requestImageForAsset:asset targetSize:CGSizeMake(100, 100) contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        image = result;
    }];
    return image;
  }

Solution 10 - Swift

Swift 5 working function

func getImageForAsset(asset: PHAsset) -> UIImage {
        let manager = PHImageManager.default
        let option = PHImageRequestOptions()
        var thumbnail = UIImage()
        option.isSynchronous = true
        manager().requestImage(for: asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, options: nil, resultHandler: {(result, info) -> Void in
                thumbnail = result!
        })
        return thumbnail
    }

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
QuestiondchengView Question on Stackoverflow
Solution 1 - SwiftdchengView Answer on Stackoverflow
Solution 2 - SwiftPatel JigarView Answer on Stackoverflow
Solution 3 - SwiftZAFAR007View Answer on Stackoverflow
Solution 4 - SwiftOhadMView Answer on Stackoverflow
Solution 5 - SwiftSour LeangChheanView Answer on Stackoverflow
Solution 6 - SwiftThomas PaulView Answer on Stackoverflow
Solution 7 - SwiftМихаил View Answer on Stackoverflow
Solution 8 - SwiftIcaroView Answer on Stackoverflow
Solution 9 - SwiftSaikiran KomirishettyView Answer on Stackoverflow
Solution 10 - SwiftNoah IarrobinoView Answer on Stackoverflow