我正在使用 UIImagePickerController 来允许用户使用相机拍照。当代理被调用时,我想将图像保存到照片库/相机胶卷中,这是使用 PHAssetChangeRequest 执行的。问题是文档说我可以使用 UIImagePickerControllerMediaMetadata 中的字典将其作为元数据包含在内,但 PHAssetChangeRequest 似乎没有参数来执行此操作。保存时如何包含此元数据?谢谢!
您可以使用
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
}
}
据我通过阅读源代码了解到,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 属性在图像中找到。
在
UIImagePickerControllerDelegate
中读出元数据和图像,如下所示:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let metadata = info[UIImagePickerController.InfoKey.mediaMetadata] as? [CFString: Any]
let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
// start processing image below
}
然后将带有元数据的图像保存到临时 URL。例如如下:
extension UIImage {
func saveAsHEIC(fileURL: URL, compressionQuality: CGFloat, metadata: [CFString: Any]?) -> Bool {
guard let cgImage = cgImage else { return false }
guard let destination = CGImageDestinationCreateWithURL(fileURL as CFURL, "public.heic" as CFString, 1, nil) else { return false }
var dict = metadata ?? [CFString: Any]()
dict[kCGImageDestinationLossyCompressionQuality] = compressionQuality
dict[kCGImagePropertyOrientation] = cgImageOrientation.rawValue
CGImageDestinationAddImage(destination, cgImage, dict as CFDictionary)
return CGImageDestinationFinalize(destination)
}
}
然后将保存在临时文件中的图像插入照片库,如下所示:
PHPhotoLibrary.shared().performChanges(
{
PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: url)
},
completionHandler: {
// ...
})
如果您想将位置数据包含到图像中,则必须使用 CoreLocation 请求位置访问,并根据规范手动填充元数据中的密钥
kCGImagePropertyGPSDictionary
。