Meet the new Photos picker
Photos & Camera 入门 16m

认识全新的照片选择器

Meet the new Photos picker

2020年6月23日

在 Apple 官方观看视频

一句话判断

iOS 14 的 PHPickerViewController 是一个全新的照片选择器——独立进程运行、不需要相册权限、支持多选和搜索,终于让「用户选一张照片」这个操作不再纠结于隐私权限了。

这场 Session 讲了什么

iOS 14 引入了全新的 Photos picker(PHPickerViewController),替代了之前的 UIImagePickerController 和手动请求相册权限然后用 PHAsset 筛选的方式。

新 Photos picker 的核心设计理念是:app 不需要获取整个相册的访问权限。用户在 picker 中选择照片后,app 只获得被选中的那些照片的访问权。这通过一个独立的进程(PHPicker 的 UI 运行在单独的 view service 中)来实现,你的 app 完全看不到用户的其他照片。

功能上,新 picker 支持:多选(可配置上限)、智能搜索(按地点、时间、人物搜索)、缩略图预览、以及直接获取原图或 Live Photo。Session 还演示了如何配置 picker 的 filter 来限制只显示特定类型的内容(如仅视频、仅截图等)。

值得深挖的点

权限模型的变化是最大的改进

PHPhotoLibrary.requestAuthorization 获取相册权限时,用户面临一个信任抉择:授权就意味着 app 能看到所有照片。很多用户因为这个而不愿意授权。新 picker 完全绕过了这个问题——它不需要任何权限。用户的选择对你来说是「授权」,未选择的照片你完全看不到。这不仅保护了隐私,还减少了用户流失。

PHPickerResult 的异步加载

选择结果 PHPickerResult 不直接包含图片数据,而是提供 NSItemProvider。你需要通过 loadObject(ofClass:) 异步加载图片。这个设计确保大图不会一次性全部加载到内存中。Session 建议在后台队列执行加载,主线程只负责 UI 更新。

代码片段

import PhotosUI

// 配置并展示新的 Photos picker
class PhotoPickerDelegate: NSObject, PHPickerViewControllerDelegate {
    
    func presentPicker(from viewController: UIViewController) {
        var configuration = PHPickerConfiguration()
        
        // 设置选择数量上限(0 = 无限制)
        configuration.selectionLimit = 5
        
        // 过滤器:只显示图片(不包括视频和 Live Photo)
        configuration.filter = .images
        
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        viewController.present(picker, animated: true)
    }
    
    // 用户选择完成后的回调
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)
        
        guard !results.isEmpty else { return }
        
        for result in results {
            // 异步加载选中的图片
            let itemProvider = result.itemProvider
            
            if itemProvider.canLoadObject(ofClass: UIImage.self) {
                itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
                    guard let image = image as? UIImage else { return }
                    DispatchQueue.main.async {
                        self?.handleSelectedImage(image)
                    }
                }
            }
        }
    }
    
    private func handleSelectedImage(_ image: UIImage) {
        // 处理用户选中的图片
    }
}
// 获取 Live Photo
func loadLivePhoto(from result: PHPickerResult) {
    let itemProvider = result.itemProvider
    
    if itemProvider.hasItemConformingToTypeIdentifier(UTType.livePhoto.identifier) {
        itemProvider.loadItem(forTypeIdentifier: UTType.livePhoto.identifier, options: nil) { item, error in
            guard let livePhoto = item as? PHLivePhoto else { return }
            DispatchQueue.main.async {
                // 显示 Live Photo
            }
        }
    }
}

最佳实践

  1. 新项目直接用 PHPickerViewController,不要再走「请求相册权限 -> 获取所有 PHAsset -> 自己做 picker」的老路。老方法需要的权限太大,用户体验差。
  2. 如果需要持续访问选中的照片,在获取 PHPickerResult 后把图片保存到 app 的沙盒目录(Application Supporttmp)。因为 picker 返回的是临时访问权限,不会持久。
  3. 多选时用 selectionLimit 控制数量。不要设为 0(无限制)然后自己在代码里截断。picker 的 UI 会根据 limit 自动调整选择行为的视觉反馈。

还有什么值得关注

  • PHPicker 的搜索功能使用的是设备上的照片智能索引(和照片 app 的搜索是同一套),不需要网络。
  • 如果你的 app 确实需要读取整个相册(比如做照片管理工具),仍然可以申请 PHPhotoLibrary 权限,PHPicker 和传统方式可以并存。
  • PHPickerConfigurationfilter 支持组合条件,比如 .any(of: [.images, .videos]) 同时显示图片和视频。
WWDC 2020