EXIF 的 PHContentEditingInputRequestOptions 块主线程
PHContentEditingInputRequestOptions for EXIF blocks main Thread
我正在获取图像 EXIF 以获取 LensMake。
这是我找到的唯一方法:
let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
lstAssets.enumerateObjects({object , index, stop in
let options = PHContentEditingInputRequestOptions()
object.requestContentEditingInput(with: options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
let fullImage : CIImage = CIImage(contentsOf: contentEditingInput!.fullSizeImageURL!)!
if let exif = fullImage.properties["{Exif}"]{ ...
问题是 requestContentEditingInput 的 callback/clasure/completion 中的代码在主线程中运行,并阻止来自 UI 的任何调用(例如按钮的 IBAction)。因此,如果我从 100 的 lstAssets.count 开始并在 enumerateObjects 中发出 100 个请求,则需要完成所有回调才能在 UI 中执行某些操作。
我尝试将 enumerateObjects 放入 DispatchQueue.global(qos: .background).async 中,但没有成功。如果我将回调代码放在后台线程中,也会发生同样的情况。
奇怪的是,如果我对图像使用带有 requestImageData 的 PHImageManager.default(),此行为会有所不同,但问题是我无法通过这种方式获取 EXIF。
如何取消阻塞队列?有没有更好的方法来获取 EXIF?
我是 运行 Swift 3 in Xcode 8.2.
更新:GCD 的奇怪行为:运行 在 PHContentEditingInputRequestOptions 取消阻塞主线程之前,enumerateObjects 中的 requestImageData,但仍然在通过单击进入下一个屏幕时 UI 等待回调结束。
如果您只需要读取 exif 元数据,您可以使用 PHImageManager
,它对后台队列更友好。
因此,您可以将代码更改为如下所示:
let operationQueue = OperationQueue()
let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
operationQueue.addOperation {
self.readPhotosMetadata(result: lstAssets, operationQueue: operationQueue)
}
func readPhotosMetadata(result: PHFetchResult<PHAsset>, operationQueue: OperationQueue) {
let imageManager = PHImageManager.default()
result.enumerateObjects({object , index, stop in
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.isSynchronous = false
imageManager.requestImageData(for: object, options: options, resultHandler: { (imageData, dataUTI, orientation, info) in
operationQueue.addOperation {
guard let data = imageData,
let metadata = type(of: self).fetchPhotoMetadata(data: data) else {
print("metadata not found for \(object)")
return
}
print(metadata)
}
})
})
}
static func fetchPhotoMetadata(data: Data) -> [String: Any]? {
guard let selectedImageSourceRef = CGImageSourceCreateWithData(data as CFData, nil),
let imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(selectedImageSourceRef, 0, nil) as? [String: Any] else {
return nil
}
return imagePropertiesDictionary
}
我们可以通过这种方式从图像中读取 Exif,代码更少,速度更快:
let firstAsset = PHAsset.fetchAssets(with: .image, options: nil).firstObject!
// For example I've used first PHAsset
firstAsset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { (eidtingInput, info) in
if let input = eidtingInput, let imgURL = input.fullSizeImageURL {
let img = CIImage(contentsOf: imgURL)!
let imgPro = img.properties
let exifDic = imgPro["{Exif}"] as! NSDictionary
print(exifDic)
}
}
我正在获取图像 EXIF 以获取 LensMake。
这是我找到的唯一方法:
let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
lstAssets.enumerateObjects({object , index, stop in
let options = PHContentEditingInputRequestOptions()
object.requestContentEditingInput(with: options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
let fullImage : CIImage = CIImage(contentsOf: contentEditingInput!.fullSizeImageURL!)!
if let exif = fullImage.properties["{Exif}"]{ ...
问题是 requestContentEditingInput 的 callback/clasure/completion 中的代码在主线程中运行,并阻止来自 UI 的任何调用(例如按钮的 IBAction)。因此,如果我从 100 的 lstAssets.count 开始并在 enumerateObjects 中发出 100 个请求,则需要完成所有回调才能在 UI 中执行某些操作。
我尝试将 enumerateObjects 放入 DispatchQueue.global(qos: .background).async 中,但没有成功。如果我将回调代码放在后台线程中,也会发生同样的情况。
奇怪的是,如果我对图像使用带有 requestImageData 的 PHImageManager.default(),此行为会有所不同,但问题是我无法通过这种方式获取 EXIF。
如何取消阻塞队列?有没有更好的方法来获取 EXIF?
我是 运行 Swift 3 in Xcode 8.2.
更新:GCD 的奇怪行为:运行 在 PHContentEditingInputRequestOptions 取消阻塞主线程之前,enumerateObjects 中的 requestImageData,但仍然在通过单击进入下一个屏幕时 UI 等待回调结束。
如果您只需要读取 exif 元数据,您可以使用 PHImageManager
,它对后台队列更友好。
因此,您可以将代码更改为如下所示:
let operationQueue = OperationQueue()
let lstAssets : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
operationQueue.addOperation {
self.readPhotosMetadata(result: lstAssets, operationQueue: operationQueue)
}
func readPhotosMetadata(result: PHFetchResult<PHAsset>, operationQueue: OperationQueue) {
let imageManager = PHImageManager.default()
result.enumerateObjects({object , index, stop in
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.isSynchronous = false
imageManager.requestImageData(for: object, options: options, resultHandler: { (imageData, dataUTI, orientation, info) in
operationQueue.addOperation {
guard let data = imageData,
let metadata = type(of: self).fetchPhotoMetadata(data: data) else {
print("metadata not found for \(object)")
return
}
print(metadata)
}
})
})
}
static func fetchPhotoMetadata(data: Data) -> [String: Any]? {
guard let selectedImageSourceRef = CGImageSourceCreateWithData(data as CFData, nil),
let imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(selectedImageSourceRef, 0, nil) as? [String: Any] else {
return nil
}
return imagePropertiesDictionary
}
我们可以通过这种方式从图像中读取 Exif,代码更少,速度更快:
let firstAsset = PHAsset.fetchAssets(with: .image, options: nil).firstObject!
// For example I've used first PHAsset
firstAsset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { (eidtingInput, info) in
if let input = eidtingInput, let imgURL = input.fullSizeImageURL {
let img = CIImage(contentsOf: imgURL)!
let imgPro = img.properties
let exifDic = imgPro["{Exif}"] as! NSDictionary
print(exifDic)
}
}