PHPickerViewController 选取照片报错处理

2024-01-11  本文已影响0人  zackzheng

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 {
                    /// 错误处理
                }
            })
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读