WWDC Quick Look 💓 By SwiftGGTeam
What's new in the Photos picker

What's new in the Photos picker

观看原视频

Highlight

Photos picker 在 iOS 16 扩展了截图、屏幕录制、慢动作、电影效果视频等过滤器,新增 all / not 复合过滤,并通过 AppKit、SwiftUI、NSOpenPanel、watchOS 把同一套隐私保护选择流程带到更多平台。


核心内容

很多 App 只需要用户选几张照片。

如果自己实现照片选择界面,App 往往要请求照片库权限,处理权限状态,维护相册 UI,还要解释为什么需要访问整座照片库。Photos picker 的思路更直接:选择界面由系统提供,并且运行在 App 进程之外。用户选中资产后,App 只拿到结果,不需要请求照片库访问权限。(00:18

WWDC 2022 的更新主要解决三个问题。

第一,过滤条件更细。以前可以筛选图片、视频、Live Photo;现在可以筛选截图、屏幕录制、慢动作视频,也可以用 PHAsset.PlaybackStyle 创建过滤器。allnot 让过滤逻辑更接近真实产品需求,例如“所有图片,但排除截图”。(00:59

第二,选择器可以进入半高 sheet 之外的工作流。iOS 16 允许通过 asset identifier 取消已选资产,也可以调用 moveAsset 调整选择顺序。这适合需要在自定义 UI 中管理已选照片的场景。(02:32

第三,Photos picker 不再只是 iOS / iPadOS API。Apple 把它带到 macOS 和 watchOS,并提供 AppKit 与 SwiftUI 接口。SwiftUI 的 PhotosPicker 可以在 iOS、iPadOS、macOS、watchOS 上使用,选择器会根据平台和可用空间选择合适布局。(03:2407:43


详细内容

用更精确的 PHPickerFilter 控制可选内容

02:04)过滤器是这场 session 最实用的更新。Justin 的例子很简单:截图拼接 App 只应该显示截图,视频编辑 App 可能只想显示特定视频类型。把无关资产挡在选择器外,用户少点几次,App 也少处理无效输入。

var configuration = PHPickerConfiguration()

// iOS 15
// Shows videos and Live Photos
configuration.filter = .any(of: [.videos, .livePhotos])

// New: iOS 15
// Shows screenshots only
configuration.filter = .screenshots

// New: iOS 15
// Shows images excluding screenshots
configuration.filter = .all(of: [.images, .not(.screenshots)])

// New: iOS 16
// Shows cinematic videos
configuration.filter = .cinematicVideos

关键点:

  • PHPickerConfiguration() 创建选择器配置,后续过滤、选择顺序、选择上限都挂在这个配置上。
  • .any(of: [.videos, .livePhotos]) 表示只要符合其中一种类型就显示,适合“视频或 Live Photo”这种并集需求。
  • .screenshots 是新增的资产类型过滤器,截图拼接、报错反馈、教程标注类 App 可以直接使用。
  • .all(of: [.images, .not(.screenshots)]) 组合了 allnot,表达“图片,同时排除截图”。
  • .cinematicVideos 面向 iOS 16 的电影效果视频;session 中说明,除 Cinematic videos、depth effect photos、bursts 之外,其他新过滤器在使用 iOS 16 SDK 编译时可回到 iOS 15 使用。(01:38

UIKit 中继续使用 PHPickerConfiguration

00:18)如果项目已经在 UIKit 中使用 PHPickerViewController,2022 年的改动不需要换掉整个入口。继续创建配置,设置过滤器、选择顺序和数量上限,再用配置创建 picker。

var configuration = PHPickerConfiguration()
configuration.filter = .images
configuration.selection = .ordered
configuration.selectionLimit = 10

let picker = PHPickerViewController(configuration: configuration)

关键点:

  • configuration.filter = .images 让选择器只显示图片。
  • configuration.selection = .ordered 要求选择结果保留用户选择顺序,适合相册、拼贴、故事板这类顺序敏感的功能。
  • configuration.selectionLimit = 10 限制最多选择 10 个项目,避免后续处理过多资产。
  • PHPickerViewController(configuration:) 用配置创建系统选择器;选择器运行在独立进程中,App 不需要请求照片库访问权限。(00:24

AppKit 版本让 macOS 也使用同一套配置

07:00)macOS 的 Photos picker 支持多选、网格缩放和搜索。AppKit API 与 UIKit 很接近,仍然使用 PHPickerConfiguration;差别是 PHPickerViewController 在 AppKit 中是 NSViewController 子类。

var configuration = PHPickerConfiguration()
configuration.filter = .images
configuration.selectionLimit = 10

let picker = PHPickerViewController(configuration: configuration)

关键点:

  • AppKit 版本复用 PHPickerConfiguration,UIKit 和 AppKit 项目可以共享大部分配置思路。
  • configuration.filter = .images 在 macOS 上同样限制选择器只显示图片。
  • configuration.selectionLimit = 10 控制多选上限。
  • session 建议 Mac 上的媒体类 App 默认使用新的 Photos picker,同时保留 NSOpenPanel 作为选择文件系统资产的入口。(05:23

NSOpenPanel 自动获得照片库入口

04:30)如果 Mac App 只是偶尔选择几张图片或视频,NSOpenPanel 可能已经足够。2022 年,NSOpenPanel 中也会出现新的照片选择 UI,并且可以选择 iCloud Photos 中的资产,不需要额外适配。

这里有一个生命周期细节:从照片库选出的文件可能随时被系统删除。长期需要这些文件时,App 应该把它们复制到自己管理的位置。(05:00

SwiftUI PhotosPicker 用 Binding 接收选择结果

07:43)SwiftUI 新增 PhotosPicker 视图。它把选择结果写入一个 binding,标签内容由调用方提供。相同代码可以用于 iOS、iPadOS、macOS、watchOS,系统会选择合适的布局。

struct ContentView: View {
    @Binding selection: [PhotosPickerItem]
    
    var body: some View {
        PhotosPicker(
            selection: $selection,
            matching: .images
        ) {
            Text("Select Photos")
        }
    }
}

关键点:

  • @Binding selection: [PhotosPickerItem] 保存选择结果;它是占位对象集合,还没有包含真实图片或视频数据。
  • PhotosPicker(selection:matching:) 创建 SwiftUI 选择器。
  • selection: $selection 把用户选择写回外部状态,适合在 view model 中响应变化。
  • matching: .images 复用过滤器语义,只显示图片。
  • Text("Select Photos") 是触发选择器的标签,可以替换成按钮、图标或自定义视图。

选择结果要按需加载

08:29PhotosPickerItem 是占位对象。真实数据要在用户选择之后加载。加载可能失败,例如设备需要从 iCloud Photos 下载资产但当前没有网络。大视频也可能下载很久,所以 session 建议显示每个项目自己的行内 loading UI,避免用一个阻塞式加载指示器盖住整个界面。

PhotosPicker 使用 Transferable 加载数据。简单场景可以直接加载 SwiftUI Image;需要控制数据类型时,可以定义自己的模型并让它遵循 Transferable。如果一次处理很多项目,或处理视频这类大文件,使用 FileTransferRepresentation 以文件形式加载,避免把全部内容放进内存。(09:2109:54

文件形式加载还有一个责任:收到文件后要复制到 App 目录,并在不再需要时删除。(10:11

watchOS 适合短流程选择图片

05:50)watchOS 也有 Photos picker,并且同样运行在独立进程中,不需要请求照片库访问权限。它支持网格和 collection 浏览,也能配置选择顺序和选择上限。

限制也很明确:watchOS picker 只显示图片。一般情况下,如果设备上超过 500 张图片,只显示最近 500 张。Family Setup 模式下,可以从 iCloud Photos 选择最近 1000 张图片;如果需要联网下载,picker 会在关闭前显示 loading UI。(06:2913:17


核心启发

1. 截图拼接入口

  • 做什么:给截图拼接 App 的导入按钮接入 Photos picker,并只显示截图。
  • 为什么值得做:session 明确把 screenshot-stitching app 作为新过滤器的目标场景,.screenshots 可以减少用户从整座相册里找截图的成本。
  • 怎么开始:创建 PHPickerConfiguration,设置 configuration.filter = .screenshots,再用 PHPickerViewController 或 SwiftUI PhotosPicker 呈现选择器。

2. 排除截图的头像选择器

  • 做什么:个人资料页只允许用户从普通照片里选头像,自动排除截图。
  • 为什么值得做:头像选择通常不需要截图;allnot 可以把规则写进选择器本身。
  • 怎么开始:使用 .all(of: [.images, .not(.screenshots)]) 作为过滤器,选择结果变化后按需加载 PhotosPickerItem

3. 跨平台资料页图片编辑

  • 做什么:把 iOS 和 macOS 的个人资料页共用一套 SwiftUI PhotosPicker 选择逻辑。
  • 为什么值得做:session 的 demo 显示,同一段 SwiftUI 代码可以在 iOS 和 macOS 编译运行,watchOS 也支持同一 API。
  • 怎么开始:在 view model 中保存 PhotosPickerItem binding,在视图里用 PhotosPicker(selection:matching:) 触发选择,再用 Transferable 加载图片。

4. 面向大视频的导入流程

  • 做什么:视频剪辑 App 选择素材后逐项显示加载状态,并把结果保存到 App 管理目录。
  • 为什么值得做:session 提醒,大视频可能下载很久,把所有数据一次性放进内存不可行。
  • 怎么开始:用 FileTransferRepresentation 以文件形式加载选中资产,收到文件后立即复制到 App 目录,并管理删除时机。

5. Mac 媒体 App 的双入口选择

  • 做什么:默认使用 Photos picker 选择照片库资产,同时保留 NSOpenPanel 选择文件系统素材。
  • 为什么值得做:session 建议媒体类 macOS App 默认使用新的 Photos picker,但用户仍可能需要选择照片库之外的文件。
  • 怎么开始:媒体库入口接入 AppKit PHPickerViewController;文件入口继续使用 NSOpenPanel,并在选择照片库文件后复制到 App 管理位置。

关联 Session

评论

GitHub Issues · utterances