使用 PhotosPicker 轻松从照片库添加图片

SwiftUI 的 PhotosPicker 组件使得从用户照片库中选择并添加图片变得简单而高效。本文将逐步指导您如何在 SwiftUI 应用中实现这一功能,提升用户体验。

使用 PhotosPicker 轻松从照片库添加图片

在开发过程中,您可能会遇到需要从用户的照片库中选择图片的情况。为此,SwiftUI 提供了一个非常方便的组件——PhotosPicker。

什么是 PhotosPicker

PhotosPicker 实在 iOS 16 和 iPadOS 16 中引入的一个新组件,旨在简化用户从照片库中选择图片的过程,开发者无需手动处理照片选择的逻辑。PhotosPicker 提供了一个简洁且功能强大的接口,能够轻松集成到 SwiftUI 应用中。

为什么使用 PhotosPicker

之前的版本中,开发者通常需要依赖 UIKit 的 UIImagePickerController 来实现类似功能,这个过程需要引入 UIKit 组件,并且开发者需要管理复杂的状态和界面更新逻辑,增加了混合框架编程的复杂性。

使用 PhotosPicker 的主要优势在于它与 SwiftUI 的深度集成,使用更少的代码实现照片选择功能,同时保持与系统原生外观一致。此外,PhotosPicker 还提供了更灵活的配置选项,允许开发者轻松指定允许的媒体类型,如仅选择图片或视频等。

如何使用 PhotosPicker 组件

基本用法

在使用 PhotosPicker 之前,首先需要在项目中导入 PhotosUI 框架,这个框架包含了所有与照片选择相关的功能,默认包含在 SwiftUI 框架中。

import PhotosUI

接下来,我们创建一个 PhotosPickerItem 类型的新变量来存储所选图像。

import SwiftUI
import PhotosUI

struct PhotosPickerView: View {

    @State private var avatarPhotoItem: PhotosPickerItem?

    var body: some View {
        VStack {
            PhotosPicker(
                "选择图片",
                selection: $avatarPhotoItem)
        }
    }
}

添加图片预览 / loadTransferable

PhotosPicker 不会自动处理显示,因此我们可以手动为它添加显示。从 PhotosPicker 中获取到的数据是 Data 类型,而 SwiftUI 的 Image 组件没有直接处理 Data 的构造函数,因此无法直接使用 Image() 组件来显示。

loadTransferable 是 photoKit 提供,可以用于从PhotosPickerItem 中加载和转换用户选择的媒体数据。非常适合处理可能需要从系统文件或资源中异步加载数据的场景,比如从照片库中提取图片。

import PhotosUI
import SwiftUI

struct PhotosPickerView: View {

    @State private var avatarPhotoItem: PhotosPickerItem?
    @State private var selectedImage: Image?

    var body: some View {
        VStack(spacing: 16) {

            // 显示选中的图片预览
            if let selectedImage = selectedImage {
                selectedImage
                    .resizable()
                    .scaledToFill()
                    .frame(width: 100, height: 100)
                    .clipShape(Circle())
                    .overlay(Circle().stroke(Color.white, lineWidth: 4))
            }

            // 当用户未选择头像时显示“选择头像”,选择后显示“更换头像”
            let buttonText = selectedImage == nil ? "选择头像" : "更换头像"

            PhotosPicker(
                buttonText, selection: $avatarPhotoItem, matching: .images)
        }
        .padding()
        .task(id: avatarPhotoItem) {
            // 使用新的方式加载和转换图片
            if let avatarPhotoItem = avatarPhotoItem {
                if let data = try? await avatarPhotoItem.loadTransferable(
                    type: Data.self),
                    let uiImage = UIImage(data: data)
                {
                    selectedImage = Image(uiImage: uiImage)
                }
            }
        }
    }
}

struct PhotosPicker_Previews: PreviewProvider {
    static var previews: some View {
        PhotosPickerView()
    }
}

它是异步的,使用 Swift 的 async/await 语法,确保在加载较大文件时不会阻塞主线程,提升应用的响应速度和用户体验。

过滤媒体类型 / matching

PhotosPicker 提供了可选的matching 参数,用于过滤用户可以从照片库中选择的媒体类型。

下面这个代码,限制用户只能选择视频类型:

import PhotosUI
import SwiftUI

struct PhotosPickerView: View {

    @State private var avatarPhotoItem: PhotosPickerItem?

    var body: some View {
        VStack {
            PhotosPicker(
                "选择视频",
                selection: $avatarPhotoItem,
                matching: .videos)
        }
    }
}

struct PhotosPicker_Previews: PreviewProvider {
    static var previews: some View {
        PhotosPickerView()
    }
}

matching 提供了多个预设的类型供选择,并且支持灵活组合使用:

.any(of: [PHPickerFilter])允许用户选择多种类型的媒体。比如,允许选择图片或视频:

matching: .any(of: [.images, .videos])

.not(PHPickerFilter):排除特定类型的媒体。比如,不允许选择图片:

matching: .not(.images)

.livePhotos:仅允许选择实况照片。

matching: .livePhotos

.videos:仅允许选择视频。

matching: .videos

.images:仅允许选择图片。

matching: .images

通过编程方式调用 .photosPicker

PhotosPicker 组件在 SwiftUI 中非常实用,能够方便用户从照片库中选择图片。然而,它是通过 UI 触发的,也就是说,必须由用户点击一个 Button 或其他触发器来打开选择器。

然而,在某些场景下,你可能希望在符合特定条件时,自动触发 PhotosPicker,而无需用户手动点击。例如,当用户首次打开应用时,如果他们还没有设置头像,你可能希望自动打开照片选择器,提示他们选择一张照片。

在这种情况下,你可以使用 .photosPicker 修饰器,并结合一个绑定到 Bool 类型的状态变量来控制选择器的显示。通过这种方式,选择器可以在编程条件满足时自动弹出,而不是依赖用户的手动操作。

官方文档中的 .photosPicker 用法如下:

.photosPicker(isPresented: $shouldShowPicker, selection: $selectedItems, matching: .images)

此外,你可以使用 .onChange 修饰器来监控 selectedItems 的变化,并在用户选择图片后,自动执行一些操作,比如加载和显示选中的图片。使用示例如下:

import SwiftUI
import PhotosUI

struct ContentView: View {
    @State private var shouldShowPicker = false
    @State private var selectedItems: [PhotosPickerItem] = []
    @State private var selectedImage: UIImage?
    
    var body: some View {
        VStack {
            if let image = selectedImage {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
                    .clipShape(Circle())
                    .overlay(Circle().stroke(Color.white, lineWidth: 4))
                    .shadow(radius: 10)
            } else {
                Text("没有选择图片")
                    .foregroundColor(.gray)
            }
            
            Button("选择图片") {
                shouldShowPicker = true
            }
        }
        .photosPicker(isPresented: $shouldShowPicker, selection: $selectedItems, matching: .images)
        .onChange(of: selectedItems) { newItems in
            Task {
                if let firstItem = newItems.first, 
                   let data = try? await firstItem.loadTransferable(type: Data.self),
                   let uiImage = UIImage(data: data) {
                    selectedImage = uiImage
                }
            }
        }
        .onAppear {
            // 示例场景:当视图首次出现时,自动打开选择器
            if selectedImage == nil {
                shouldShowPicker = true
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

更多开发文章:

SwiftData 教程与实例:简化 iOS 开发数据持久化(一)
SwiftData 是 Apple 为 iOS 开发者提供的新型类 ORM 工具,简化数据库管理,无需编写 SQL,本文包含 SwiftData 教程和使用实例。
使用 Figma 进行 visionOS App 原型设计(一)
了解原型设计工具 Figma 的基础用法,以及如何使用它来进行 visionOS 应用的原型设计。