【问题标题】:PHAsset: Saving a new image with metadataPHAsset:使用元数据保存新图像
【发布时间】:2015-11-30 02:34:10
【问题描述】:

我正在使用 UIImagePickerController 来允许用户使用相机拍照。当代理被调用时,我想将图像保存到照片库/相机胶卷,我使用 PHAssetChangeRequest 执行此操作。问题是文档说我可以使用 UIImagePickerControllerMediaMetadata 中的字典将其包含为元数据,但 PHAssetChangeRequest 似乎没有执行此操作的参数。保存时如何包含此元数据?谢谢!

【问题讨论】:

  • 你找到办法了吗?
  • 很遗憾没有。文档要么非常不透明,要么错误。
  • 我知道我无法相信这个库有多糟糕,根本不符合 Apple 的标准

标签: ios uiimagepickercontroller ios9 phasset phphotolibrary


【解决方案1】:

您可以使用PHAssetCreationRequest。为了使用PHAssetCreationRequest,您将合并imageData和元数据,然后将imageDataWithMetadata写入相册。像这样:

if let imageDataWithMetadata = self.writeMetadata(metadata, into: imageData) {
    self.saveImageDataForiOS9(imageDataWithMetadata)
}

func saveImageDataForiOS9(_ data: Data) {
    var newImageIdentifier: String!

    PHPhotoLibrary.shared().performChanges({
        if #available(iOS 9.0, *) {
            let assetRequest = PHAssetCreationRequest.forAsset()
            assetRequest.addResource(with: .photo, data: data, options: nil)
            newImageIdentifier = assetRequest.placeholderForCreatedAsset!.localIdentifier
        } else {
            // Fallback on earlier versions
        }
    }) { (success, error) in
        DispatchQueue.main.async(execute: {
            if success, let newAsset = PHAsset.fetchAssets(withLocalIdentifiers: [newImageIdentifi
                // ...
            } else {
                // ...
            }
        })

    }
}

对于 iOS 8,您可以使用 ALAssetsLibrary 来保存带有元数据的新图像:

func saveImageDataForiOS8(_ imageData: Data, _ metadata: Dictionary<AnyHashable, Any>?) {
    let library = ALAssetsLibrary()
    library.writeImageData(toSavedPhotosAlbum: imageData, metadata: metadata, completionBlock: { (newURL, error) in
        if let _ = error {
            // ...
        } else {
            if let newAsset = PHAsset.fetchAssets(withALAssetURLs: [newURL!], options: nil).firstObject {
                // ...
            }
        }
    })
}

所以,最后你会得到这样的代码:

func saveImage(_ imageData: Data, metadata: Dictionary<AnyHashable, Any>?) {
    if #available(iOS 9.0, *) {
        if let metadata = metadata {
            if let imageDataWithMetadata = self.writeMetadata(metadata, into: imageData) {
                self.saveImageDataForiOS9(imageDataWithMetadata)
            } else {
                self.saveImageDataForiOS9(imageData)
            }
        } else {
            self.saveImageDataForiOS9(imageData)
        }
    } else {
        self.saveImageDataForiOS8(imageData, metadata)
    }
}

func saveImageDataForiOS8(_ imageData: Data, _ metadata: Dictionary<AnyHashable, Any>?) {
    let library = ALAssetsLibrary()
    library.writeImageData(toSavedPhotosAlbum: imageData, metadata: metadata, completionBloc
        if let _ = error {
            // ...
        } else {
            if let newAsset = PHAsset.fetchAssets(withALAssetURLs: [newURL!], options: nil).
                // ...
            }
        }
    })
}

func saveImageDataForiOS9(_ data: Data) {
    var newImageIdentifier: String!

    PHPhotoLibrary.shared().performChanges({
        if #available(iOS 9.0, *) {
            let assetRequest = PHAssetCreationRequest.forAsset()
            assetRequest.addResource(with: .photo, data: data, options: nil)
            newImageIdentifier = assetRequest.placeholderForCreatedAsset!.localIdentifier
        } else {
            // Fallback on earlier versions
        }
    }) { (success, error) in
        DispatchQueue.main.async(execute: {
            if success, let newAsset = PHAsset.fetchAssets(withLocalIdentifiers: [newImageId
                // ...
            } else {
                // ...
            }
        })

    }
}

internal func writeMetadata(_ metadata: Dictionary<AnyHashable, Any>, into imageData: Data) 
    let source = CGImageSourceCreateWithData(imageData as CFData, nil)!
    let UTI = CGImageSourceGetType(source)!

    let newImageData = NSMutableData()
    if let destination = CGImageDestinationCreateWithData(newImageData, UTI, 1, nil) {
        CGImageDestinationAddImageFromSource(destination, source, 0, metadata as CFDictionar
        if CGImageDestinationFinalize(destination) {
            return newImageData as Data
        } else {
            return nil
        }
    } else {
        return nil
    }
}

【讨论】:

  • 我不确定 CGImageDestinationAddImageFromSource 是否可以这样工作。至少根据文档,它没有。
  • @DagÅgren 我认为 CGImageDestinationAddImageFromSource 效果很好。我在这个库中使用了这个方法好几个月了:github.com/zhangao0086/DKImagePickerController/blob/develop/…
  • 这就是我要找的。节省了我的一周
【解决方案2】:

从我通过阅读源代码可以理解的情况来看,Apple 所说的元数据是指资产的属性。

open var pixelWidth: Int { get }
open var pixelHeight: Int { get }
open var creationDate: Date? { get }
open var modificationDate: Date? { get }
open var location: CLLocation? { get }
open var duration: TimeInterval { get }
open var isHidden: Bool { get }

通过访问 CGImage 属性,可以在图像中找到其他元数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-01
    • 2015-01-23
    • 2012-11-01
    • 2021-09-08
    • 2012-07-08
    • 1970-01-01
    相关资源
    最近更新 更多