PHAsset 转 UIImage

64

我正在尝试从PHAsset创建UIImage(比如缩略图之类的),以便可以将其传递到需要UIImage的地方。我已经尝试过调整我在SO上找到的解决方案(因为它们都直接将其传递到例如tableview之类的东西中),但是我没有成功(可能是因为我做错了什么)。

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
}

println提示我manager.request这一行目前没有执行任何操作。我该如何获得作为UIImage的资产。

谢谢。


例如,假设我想要执行以下操作: var image = a.getAssetThumbnail(firstAsset)我该如何实现? - dcheng
如果任何人需要更多信息,请查看苹果的示例代码:https://developer.apple.com/documentation/photokit/browsing_and_modifying_photo_albums - iAleksandr
11个回答

93

这正是我所需要的,如果还有其他人需要,可以参考一下。

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
}

编辑:Swift 3 更新

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
}

4
您如何获取PHAsset的原始宽度+高度? - Tim Nuwin
我也想知道如何获取原始宽度和高度,@TimNuwin,你找到解决方案了吗? - Bram Roelandts
1
如果您在谈论pixelWidth和pixelHeight属性,那么这就是您需要使用的内容。如果不是,请提供更多信息以便我帮助您。希望以下链接能够帮到您: https://developer.apple.com/library/ios/documentation/Photos/Reference/PHAsset_Class/ - dcheng
8
通过将目标大小(targetSize)设置为PHImageManagerMaximumSize,您可以获得原始图像。 - Borut Tomazin
@BorutTomazin 当我更改'targetSize'时,我似乎遇到了一个奇怪的崩溃:此应用程序正在从后台线程修改自动布局引擎,此前已经从主线程访问该引擎。这可能会导致引擎损坏和奇怪的崩溃。 - luke
这个返回的缩略图是否包含像位置或创建日期之类的元数据? - Bhavin Ramani

35

试试这个方法,它对我有效,希望能对你有所帮助。

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
}

1
我遇到了错误 "无法加载资产<PHAsset>的图像数据"。 - Sneha
它对我没有帮助,但非常好的尝试。 - Mahesh Joya

27

简单解决方案(Swift 4.2)

方法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 

如果您只需要从PHAsset获取UIImage,请使用此方法。

或者

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?)

使用此方法获取您所需的 UIImage

或者

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")
})

使用此方法在完成后进行通知。

方法二:

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

如果您想要PHAssetUIImage和结果信息,请使用此方法。

或者

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?)

使用此方法来获取所需数据。


这个需要更高的赞。很棒的解决方案。 - snarik

9

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
    }
}

8

Swift 4.

resizeModedeliveryMode - 这些可以根据用户的需求进行设置。

isNetworkAccessAllowed - 将其设置为“true”以从云中获取图像。

imageSize- 所需的图像尺寸。

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!)
    })
}

7

对于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
}

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

这是一个获取 iOS 应用程序中资源缩略图的 Swift 代码片段。该代码使用 PHImageManager 和 PHImageRequestOptions 类来异步获取资源的缩略图。


7
我建议使用苹果的PHCachingImageManager(继承自PHImageManager):

PHCachingImageManager对象可获取或生成照片或视频资源的图像数据。

此外,PHCachingImageManager支持更好的缓存机制。

以下是同步获取缩略图的示例:

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*
                                                  }

此外,您可以使用 PHCachingImageManager 对象进行图片缓存,以提高 UI 响应速度:
要使用缓存图片管理器,请按以下步骤操作:
1. 创建一个 PHCachingImageManager 实例(此步骤替换了使用 shared PHImageManager 实例的方法)。
2. 使用 PHAsset 类方法获取您感兴趣的资产。
3. 调用 startCachingImagesForAssets:targetSize:contentMode:options: 方法来为这些资产准备图像,并传递您在稍后请求每个单独资产的图像时计划使用的目标大小、内容模式和选项。
4. 当您需要单个资产的图像时,请调用 requestImageForAsset:targetSize:contentMode:options:resultHandler: 方法,并传递您在准备该资产时使用的相同参数。
如果您请求的图像已经准备好,则 PHCachingImageManager 对象将立即返回该图像。否则,Photos 根据需要准备图像并将其缓存供以后使用。
在我们的示例中:
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)

4
问题在于requestImageForAsset是一个resultHandler,在您的函数已经打印并返回您期望的值之后才发生。我做了一些更改来展示这种情况,并建议一些简单的解决方案。
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")
}

请在苹果文档了解更多有关闭包、完成处理程序和异步函数的信息。

希望这能对您有所帮助!


还是有点困惑。 那么我们要将getAssetThumbnail更改为不返回任何东西吗?在这种情况下,我们该如何传递retimage到我们需要的位置呢? 什么时候以及如何执行该块,以便我们可以获得一些有用的内容。希望callReturnImage与我们试图传递retimage的其他函数是分开的。 - dcheng
你无法返回任何东西,因为你的返回将在检索之前发生。你可以添加另一个函数来执行您想要对图像执行的操作,或者将您想要对图像执行的操作放入完成块中。这两种方法都是解决此问题的较简单的方式。 - Icaro
好的。假设我有两个函数(x和y),它们都需要UIImages作为参数。我想让它们操作我们在该块中创建的UIImage。如果我将UIImage传递给x和y,并从请求方法中获取该图像,那么我是否无法在该块之外执行此操作?或者我必须将x和y作为您提示的callReturnImage函数调用。如果不可能的话,那么如果我将来创建函数z,难道我不必返回并编辑此函数才能使其正常工作吗? - dcheng
我并不是说这是不可能的,我是在说这是更简单的方法。你可以创建一个也返回完成块的函数,但问题只会变得更加复杂。我会在答案中发布一个链接,让你了解更多相关信息。 - Icaro

1

基于dcheng答案的Objective-C版本代码。

-(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;
  }

1

Swift 5 工作函数

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
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接