Search code examples
swiftphotokit

How can I convert a PHLivePhoto to UIImage?


In my app I have a section of code where I need to account for a PHLivePhoto type object and convert this to a UIImage. I believe it has to do with a PHAsset, PHAssetResource, or PHImageManager but unclear how to perform the conversion. What's a good approach on how to convert from PHLivePhoto -> UIImage? Thanks for the help!

if let livePhoto = object as? PHLivePhoto {
    let livePhotoResources = PHAssetResource.assetResources(for: livePhoto)
    print("\(livePhotoResources)")
    
    // imageBucket is of type UIImage[]
    // livePhoto is of type PHLivePhoto but I don't know how to convert this to type PHAsset
    viewModel.imageBucket.append(convertImageAsset(asset: **WHAT_DO_I_INSERT_HERE**))
}

...

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

results in:

[<PHAssetResource: 0x28225cdc0> {
    type: photo
    uti: public.heic
    filename: IMG_5442.heic
    asset: (null)
    locallyAvailable: YES
    fileURL: file:///private/var/mobile/Containers/Data/Application/2FB56305-7600-4A8E-9C67-A71B4A4A9607/tmp/live-photo-bundle/75FD3D97-F13E-4F79-A6E9-F0743D443FDD.pvt/IMG_5442.HEIC
    width: 0
    height: 0
    fileSize: 0
    analysisType: unavailable
    cplResourceType: Unknown
    isCurrent: NO
    isInCloud: NO
}, <PHAssetResource: 0x28226bc00> {
    type: video_cmpl
    uti: com.apple.quicktime-movie
    filename: IMG_5442.mov
    asset: (null)
    locallyAvailable: YES
    fileURL: file:///private/var/mobile/Containers/Data/Application/2FB56305-7600-4A8E-9C67-A71B4A4A9607/tmp/live-photo-bundle/75FD3D97-F13E-4F79-A6E9-F0743D443FDD.pvt/IMG_5442.MOV
    width: 0
    height: 0
    fileSize: 0
    analysisType: unavailable
    cplResourceType: Unknown
    isCurrent: NO
    isInCloud: NO
}]

Solution

  • You need to use PHAsset to fetch the asset, then request image data from PHImageManager.default()

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        dismiss(animated: true)
        
        guard let assetIdentifier = results.first?.assetIdentifier  else {
            return
        }
        
        if let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil).firstObject {
            PHImageManager.default().requestImageDataAndOrientation(for: phAsset, options: nil) { [weak self] data, _, _, _ in
                if let data = data, let image = UIImage(data: data) {
                    self?.viewModel.imageBucket.append(image)
                }
            }
        }
    }
    

    To get assetIdentifier, you need to create PHPickerConfiguration object using the shared photo library. Creating a configuration without a photo library provides only asset data, and doesn't include asset identifiers.

    var configuration = PHPickerConfiguration(photoLibrary: .shared())
    // Set the filter type according to the user’s selection. .images is a filter to display images, including Live Photos.
    configuration.filter = .images
    // Set the mode to avoid transcoding, if possible, if your app supports arbitrary image/video encodings.
    configuration.preferredAssetRepresentationMode = .current
    // Set the selection limit.
    configuration.selectionLimit = 1
        
    let picker = PHPickerViewController(configuration: configuration)
    picker.delegate = self
    present(picker, animated: true)