SwiftUI 图片选择组件 PhotosPicker 全解析

一、引言

在日常的 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 方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值