PHPickerViewController,来自 iOS 14 推出的 PhotoKit 框架,可替代 UIImagePickerController。
苹果在宣传中说它是基于系统的相册 App 的,具有与系统相册一致的 UI 和操作方式,可以保证用户体验的一致性。并且和相册 App 一样,支持通过人物、地点等关键信息来搜索照片。并且 PHPicker 是在独立进程中运行的,与宿主 App 无关,宿主 App 也无法通过截屏 api 来获取当前屏幕上的照片信息。为了保护用户隐私,苹果真的是在各种细节上严防死守。
获取照片
class ViewController: UIViewController {
/// 选择照片
func selectPhoto() {
var configuration = PHPickerConfiguration()
configuration.filter = .images // 只显示图片
configuration.selectionLimit = 1 // 最多允许选择的数量,默认值为1
configuration.preferredAssetRepresentationMode = .automatic // 自动根据设备和资源类型选择最佳预览模式
let picker = PHPickerViewController(configuration: configuration)
picker.modalPresentationStyle = .fullScreen
picker.delegate = self
present(picker, animated: true, completion: nil)
}
}
// MARK: - PHPickerViewControllerDelegate
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
self.presentedViewController?.dismiss(animated: true)
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
guard let self = self else { return }
guard let image = image as? UIImage else {
/// 错误处理
return
}
self.handle(uiImage: image)
}
}
}
选取手机拍的照片可以正常转换成 UIImage,但是选取网络下载的照片。
报错如下:
Could not coerce an item to class UIImage
报错尝试
loadObject 方法的第一个参数类型是 NSItemProviderReading 协议,文档没有提示什么类型遵循该协议。
发现 NSItemProvider 有个 canLoadObject 方法,可以判断是否可以转换。和 loadObject 方法的参数类型一样,都是 NSItemProviderReading 类型。
let a = result.itemProvider.canLoadObject(ofClass: AVURLAsset.self)
let b = result.itemProvider.canLoadObject(ofClass: NSString.self)
let c = result.itemProvider.canLoadObject(ofClass: NSURL.self)
let d = result.itemProvider.canLoadObject(ofClass: NSUserActivity.self)
let e = result.itemProvider.canLoadObject(ofClass: UIImage.self)
发现网络下载的照片,这几种类型都无法转换。
使用 Data 提示不符合 NSItemProviderReading,尝试 NSData 也不行。
let f = result.itemProvider.canLoadObject(ofClass: NSData.self)
报错如下:
Instance method 'loadObject(ofClass:completionHandler:)' requires that 'NSData' conform to '_ObjectiveCBridgeable'
发现 NSItemProvider 有另一个方法 loadFileRepresentation,completionHandler 回调的第一个参数是 URL,于是试了下,可以获取到图片的本地 URL。
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier,
completionHandler: { [weak self] url, error in
do {
if let url = url {
let imageData = try Data(contentsOf: url)
if let image = UIImage(data: imageData) {
self?.handle(image: image)
} else {
/// 错误处理
}
} else {
/// 错误处理
}
} catch let error {
/// 错误处理
}
})
最终处理
// MARK: - PHPickerViewControllerDelegate
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
self.presentedViewController?.dismiss(animated: true)
guard let result = results.first else {
return
}
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
guard let self = self else { return }
guard let image = image as? UIImage else {
/// 错误处理
return
}
self.handle(image: image)
}
} else {
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier,
completionHandler: { [weak self] url, error in
do {
if let url = url {
let imageData = try Data(contentsOf: url)
if let image = UIImage(data: imageData) {
self?.handle(image: image)
} else {
/// 错误处理
}
} else {
/// 错误处理
}
} catch let error {
/// 错误处理
}
})
}
}
}
网友评论