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

What's new in the Photos picker

观看原视频

Highlight

iOS 17 的 Photos Picker 新增嵌入式样式(inline)、连续选择(continuous selection)和配件可见性控制,让开发者可以将系统照片选择器无缝集成到应用界面中,无需请求照片库权限。

核心内容

自建照片选择器是一件费力不讨好的事。你需要处理相册浏览、搜索、缩放网格、权限请求,还要让用户放心你不会偷看他们的照片。

00:22)iOS 14 引入的系统 Photos Picker 解决了这些问题。它运行在独立进程中,应用只能获取用户实际选中的照片,无需请求照片库访问权限。

但之前的 Picker 只能以模态 sheet 方式呈现。iOS 17 让 Picker 可以直接嵌入到应用的 SwiftUI 视图层级中。

嵌入式选择器

02:47.photosPickerStyle(.inline) 让 Picker 嵌入应用界面。它仍然运行在独立进程中,但视觉上和应用融为一体。

首次展示嵌入式选择器时,系统会自动显示引导 UI,告知用户应用只能访问选中的照片。选择器上还有隐私徽章标识。

三种样式

06:11

  • .presentation:默认的模态 sheet 样式
  • .inline:嵌入样式,占满指定区域
  • .compact:单行横向滚动,适合空间受限的 UI

连续选择

02:40selectionBehavior: .continuous 让选择实时通知应用。用户每选一张或取消一张,应用立刻收到更新。嵌入模式下不再需要”添加”按钮。

配件控制

02:11.photosPickerAccessoryVisibility 控制导航栏、工具栏、侧边栏的显示。.photosPickerDisabledCapabilities 可以禁用搜索、相册导航、暂存区等功能,用自定义 UI 替代。

Options 菜单

11:21)新的 Options 菜单让用户可以选择是否分享敏感元数据(如位置信息)。使用 PhotosPickerPHPickerViewController 的应用自动支持,无需额外工作。

详细内容

嵌入式 Photos Picker(SwiftUI)

02:47)将 Picker 嵌入应用界面:

import SwiftUI
import PhotosUI

struct ContentView: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        VStack {
            // 嵌入的 Picker
            PhotosPicker(selection: $selectedItems,
                        selectionBehavior: .continuous) {
                // 不显示标签,因为 Picker 本身会渲染内容
            }
            .photosPickerStyle(.inline)
            .photosPickerAccessoryVisibility(.hidden, edges: .all)
            .photosPickerDisabledCapabilities([.search, .selectionActions])
            .frame(height: 300)
            .ignoresSafeArea(.container, edges: .bottom)
            
            // 显示已选择的照片
            SelectedPhotosList(items: selectedItems)
        }
    }
}

关键点:

  • .photosPickerStyle(.inline) 切换到嵌入模式
  • .photosPickerAccessoryVisibility(.hidden, edges: .all) 隐藏导航栏和工具栏
  • .photosPickerDisabledCapabilities([.search, .selectionActions]) 禁用搜索和选择按钮
  • selectionBehavior: .continuous 实现实时选择更新
  • .ignoresSafeArea 让 Picker 延伸到屏幕底部

UIKit/AppKit 中的嵌入式 Picker

10:02)在 UIKit 中使用嵌入模式:

import PhotosUI

class ViewController: UIViewController {
    func setupEmbeddedPicker() {
        var configuration = PHPickerConfiguration(photoLibrary: .shared())
        configuration.selection = .continuous
        configuration.mode = .compact // 或 .default
        configuration.edgesWithoutContentMargins = [.top, .bottom]
        configuration.disabledCapabilities = [.search]
        
        let picker = PHPickerViewController(configuration: configuration)
        addChild(picker)
        view.addSubview(picker.view)
        
        // 使用 Auto Layout 定位
        picker.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            picker.view.topAnchor.constraint(equalTo: view.topAnchor),
            picker.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            picker.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            picker.view.heightAnchor.constraint(equalToConstant: 300)
        ])
        
        picker.didMove(toParent: self)
    }
}

关键点:

  • PHPickerConfigurationselection 设为 .continuous 启用连续选择
  • mode 设为 .compact 使用单行布局
  • edgesWithoutContentMargins 隐藏指定边缘的配件
  • 需要作为 child view controller 添加

动态更新 Picker 配置

11:04)使用 PHPickerConfiguration.Update 在 Picker 显示时更新配置:

// 更新选择限制
var update = PHPickerConfiguration.Update()
update.selectionLimit = 5
picker.update(with: update)

// 取消选择某个资源
picker.deselectAsset(withIdentifier: assetIdentifier)

// 重新排序已选资源
picker.moveAsset(withIdentifier: identifier, toIndex: newIndex)

关键点:

  • PHPickerConfiguration.Update 允许运行时修改配置
  • deselectAssetmoveAsset 管理已选资源

接收 HDR 和原始格式照片

12:16)获取原始格式的照片,避免自动转码:

import UniformTypeIdentifiers

struct PhotoPicker: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        PhotosPicker(selection: $selectedItems,
                    matching: .images) {
            Text("选择照片")
        }
        .onChange(of: selectedItems) { items in
            for item in items {
                // 请求原始格式
                item.loadTransferable(type: .image,
                                     preferredItemEncoding: .current) { result in
                    switch result {
                    case .success(let data):
                        // data 包含原始格式的图像数据
                        break
                    case .failure(let error):
                        print(error)
                    }
                }
            }
        }
    }
}

关键点:

  • preferredItemEncoding: .current 避免自动转码为 JPEG
  • 请求 .image 类型获取原始格式
  • 请求特定类型如 .jpeg 仍可能触发转码

处理 Cinematic 模式视频

13:24)Picker 默认返回 Cinematic 视频的渲染版本(深度效果已烘焙)。如需获取可编辑的决策点,需要请求照片库权限并通过 PhotoKit 获取:

// Picker 返回的是渲染后的视频
// 如需原始 Cinematic 数据,使用 PhotoKit:

let fetchOptions = PHFetchOptions()
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: fetchOptions)

if let asset = assets.firstObject {
    // 检查是否是 Cinematic 视频
    if asset.mediaSubtypes.contains(.videoCinematic) {
        // 通过 PHImageManager 请求原始视频资源
    }
}

关键点:

  • Picker 返回的 Cinematic 视频是渲染版本
  • 可编辑版本需要 PhotoKit 权限
  • 参考 “Support Cinematic mode videos in your app” session

配件可见性控制

04:52)控制 Picker 的 UI 元素:

// 隐藏所有配件
.photosPickerAccessoryVisibility(.hidden, edges: .all)

// 只隐藏底部工具栏
.photosPickerAccessoryVisibility(.hidden, edges: .bottom)

// iPadOS/macOS 隐藏侧边栏
.photosPickerAccessoryVisibility(.hidden, edges: .leading)

配件定义:

  • iOS:顶部是导航栏,底部是工具栏
  • iPadOS/macOS:左侧是侧边栏,顶部和底部同 iOS

禁用 Picker 功能

05:12)禁用特定功能:

.photosPickerDisabledCapabilities([.search, .collectionNavigation, .stagingArea])

可禁用的功能:

  • .search:隐藏搜索栏
  • .collectionNavigation:隐藏相册标签页和侧边栏
  • .stagingArea:替换工具栏按钮为状态标签
  • .selectionActions:隐藏”取消”和”添加”按钮(需要配合 .continuous 选择)

核心启发

1. 为社交应用构建无缝的图片选择体验

做什么:在发布动态或评论的界面中直接嵌入 Photos Picker,用户选择照片时实时显示在输入区域下方。

为什么值得做:传统 sheet 模式会打断用户的发布流程。嵌入式 Picker 让用户在选择照片的同时继续编辑文字,体验更流畅。

怎么开始:使用 .photosPickerStyle(.inline) 嵌入 Picker,设置 selectionBehavior: .continuous 实现实时更新,隐藏导航栏和工具栏让 Picker 融入应用设计。

2. 为图片编辑应用添加紧凑选择条

做什么:在图片编辑工具栏上方添加一个紧凑的横向照片选择条,让用户快速切换要编辑的照片。

为什么值得做:.compact 样式只占一行高度,适合空间受限的工具型界面。用户不需要离开编辑界面就能选择新照片。

怎么开始:使用 PHPickerConfigurationmode: .compact,设置较小的固定高度,监听选择变化加载新图片。

3. 构建隐私优先的照片管理应用

做什么:完全使用 Photos Picker 替代直接请求照片库权限,让用户精确控制分享哪些照片。

为什么值得做:Options 菜单让用户可以移除位置等敏感元数据。Picker 的独立进程设计确保应用只能访问选中的照片,建立用户信任。

怎么开始:使用 PhotosPickerTransferable API 接收图片,设置 preferredItemEncoding: .current 保留原始格式和 HDR 信息。避免使用 UIImagePickerController 或请求完整照片库权限。

4. 为相册应用添加自定义相册导航

做什么:禁用 Picker 内置的相册导航,使用自己的侧边栏或下拉菜单来切换相册,同时保留 Picker 的网格和搜索功能。

为什么值得做:应用可能有特殊的相册组织方式(如按项目、按时间线),自定义导航可以更好地融入应用的整体信息架构。

怎么开始:使用 .photosPickerDisabledCapabilities(.collectionNavigation) 隐藏内置相册导航,在 Picker 旁边放置自定义的相册列表,通过 PhotoKit 获取相册内容并传递给 Picker。

关联 Session

评论

GitHub Issues · utterances