一、引言
在日常的 App 开发中,从相册选择图片或视频几乎是个“标配”功能。无论是用户上传头像、添加封面,还是提交照片/视频素材,一个好用的选择器组件都能极大提升用户体验。
而在 iOS 16 之后,SwiftUI 开发者终于迎来了原生的图库选择器 —— PhotosPicker!
相比老旧的 UIImagePickerController 和 PHPickerViewController,PhotosPicker 具备这些特点:
- 原生支持 SwiftUI,写法现代且响应式;
- 异步加载资源,兼容 iCloud;
- 支持多图、多视频、类型筛选、数量限制等功能;
- 使用简单,无需处理繁琐的 delegate。
这篇文章我们将基于一个完整 demo,快速上手并演示以下几个常见场景:
- ✅ 选择一张图片
- ✅ 选择多张图片
- ✅ 选择一个视频
- ✅ 选择多个视频
每个场景都包含展示效果和代码讲解,直接拿来就能用。
二、选择图片的使用方式
我们先来看图片选择的功能,包括 单图选择 和 多图选择 两种常见场景。通过 PhotosPicker 的简单配置,我们就能轻松实现这两类需求。
1️⃣ 选择一张图片
要选择单张图片,只需要使用一个 @State 变量绑定选中的 PhotosPickerItem,并在 .onChange 中通过异步方式加载出 UIImage。
👇 示例代码:
@State private var singleImageItem: PhotosPickerItem?
@State private var singleImage: UIImage?
VStack {
if let image = singleImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(height: 200)
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(height: 200)
.overlay(Text("No Image Selected"))
}
PhotosPicker("选择一张图片", selection: $singleImageItem, matching: .images)
.onChange(of: singleImageItem) { newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self),
let uiImage = UIImage(data: data) {
singleImage = uiImage
}
}
}
}
🔍 说明:
- selection 绑定到 PhotosPickerItem?,表示最多只选一个资源;
- matching: .images 只允许用户选取图片;
- 使用 loadTransferable(type: Data.self) 异步加载图片数据;
- 最终将选中的图片展示在界面上。
2️⃣ 选择多张图片
如果你希望用户一次选择多张图,只需要:
- 将 selection 改为绑定 [PhotosPickerItem]
- 添加 maxSelectionCount 参数(比如 5 张)
- 遍历所有选中的项,逐个异步加载
👇 示例代码:
@State private var multipleImageItems: [PhotosPickerItem] = []
@State private var multipleImages: [UIImage] = []
VStack {
ScrollView(.horizontal) {
HStack {
ForEach(multipleImages, id: \.self) { image in
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
}
}
}
PhotosPicker("选择多张图片", selection: $multipleImageItems, maxSelectionCount: 5, matching: .images)
.onChange(of: multipleImageItems) { newItems in
Task {
multipleImages.removeAll()
for item in newItems {
if let data = try? await item.loadTransferable(type: Data.self),
let uiImage = UIImage(data: data) {
multipleImages.append(uiImage)
}
}
}
}
}
📌 说明:
- maxSelectionCount 可设定允许最多选择多少张;
- 支持横向滚动展示选中的图片;
- 多图加载推荐使用异步 Task 避免阻塞主线程。
三、选择视频的使用方式
PhotosPicker 不仅可以选择图片,也支持选择本地视频文件,并且同样支持单选与多选。我们只需将筛选条件 matching 设置为 .videos 即可。
1️⃣ 选择一个视频
选择单个视频时,核心逻辑与图片类似,只是获取的类型改为 URL(或者 Data),用于后续播放或上传。
👇 示例代码:
@State private var singleVideoItem: PhotosPickerItem?
@State private var singleVideoURL: URL?
VStack {
if let url = singleVideoURL {
Text("选中视频:\(url.lastPathComponent)")
.lineLimit(1)
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(height: 50)
.overlay(Text("No Video Selected"))
}
PhotosPicker("选择一个视频", selection: $singleVideoItem, matching: .videos)
.onChange(of: singleVideoItem) { newItem in
Task {
print("视频选择变更:\(String(describing: newItem))")
if let url = try? await newItem?.loadTransferable(type: URL.self) {
singleVideoURL = url
} else if let data = try? await newItem?.loadTransferable(type: Data.self) {
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".mov")
try? data.write(to: tempURL)
singleVideoURL = tempURL
}
}
}
}
📌 说明:
- 有些视频项可能不直接支持 URL,需要 fallback 处理:通过 Data 写入临时文件再使用;
- 此处我们仅展示文件名,你也可以使用 AVPlayer 播放视频或获取封面图;
2️⃣ 选择多个视频
多视频选择逻辑基本相同,只需使用 [PhotosPickerItem],并设置 maxSelectionCount。
👇 示例代码:
@State private var multipleVideoItems: [PhotosPickerItem] = []
@State private var multipleVideoURLs: [URL] = []
VStack {
ForEach(multipleVideoURLs, id: \.self) { url in
Text(url.lastPathComponent)
.lineLimit(1)
}
PhotosPicker("选择多个视频", selection: $multipleVideoItems, maxSelectionCount: 5, matching: .videos)
.onChange(of: multipleVideoItems) { newItems in
Task {
multipleVideoURLs.removeAll()
for item in newItems {
print("处理视频项:\(item)")
if let url = try? await item.loadTransferable(type: URL.self) {
multipleVideoURLs.append(url)
} else if let data = try? await item.loadTransferable(type: Data.self) {
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".mov")
try? data.write(to: tempURL)
multipleVideoURLs.append(tempURL)
}
}
}
}
}
🔍 说明:
- 多视频选择结果会以列表形式展示;
- 和图片一样,每个视频都需异步加载,建议在加载前清空旧数据;
- 如果你需要限制视频时长,目前只能借助 PHPickerConfiguration + UIKit 实现(SwiftUI 原生还不支持时长过滤)。
四、结语
通过本文的几个实战示例,我们已经掌握了 PhotosPicker 在 SwiftUI 中的基本用法,包括:
- 选择一张图片
- 选择多张图片
- 选择一个视频
- 选择多个视频
使用起来非常自然简洁,完全贴合 SwiftUI 的数据驱动设计。
需要特别注意的是,PhotosPicker 所返回的资源加载是异步的,通常需要借助 Task 来执行 await loadTransferable(...)。这意味着我们可以灵活地加载图片数据、视频地址或原始文件,并按需做展示、上传或保存等操作。
总的来说,PhotosPicker 是 SwiftUI 项目中处理图片/视频选择的不二之选。只要你的项目支持 iOS 16 及以上系统,就可以放心使用它替代旧的 UIKit 方案。