精通 SwiftUI - iOS 17 版

第 53 章
如何在 iOS App 中嵌入照片選擇器

從iOS 16開始,SwiftUI引入了一個名為PhotosPicker的本地照片選取器視圖。如果你的App需要訪問使用者的照片庫,PhotosPicker視圖可以無縫地管理照片選擇過程。這個內建視圖提供了非凡的簡單性,讓開發人員只需幾行程式碼就可以呈現選取器並處理圖像選擇。

當呈現PhotosPicker視圖時,它會在你的應用界面之上以一個獨立的頁面形式展示照片相冊。在iOS的早期版本中,你無法自定義或更改照片選取器視圖的外觀,以符合你的應用程序佈局。然而,蘋果在iOS 17中對PhotosPicker視圖進行了增強,使開發人員可以將其無縫嵌入應用程序中。此外,你可以使用標準的SwiftUI修飾器(如.frame.padding)來修改其大小和佈局。

在本教學中,我將向你展示如何使用改進的PhotosPicker視圖實現內嵌的照片選取器。

重温 PhotosPicker

要使用 PhotosPicker 視圖,你可以首先聲明一個狀態變量來存儲照片選擇,然後通過將綁定傳遞給該狀態變量來實例化一個 PhotosPicker 視圖。以下是一個示例:

import SwiftUI
import PhotosUI

struct ContentView: View {

    @State private var selectedItem: PhotosPickerItem?

    var body: some View {
        PhotosPicker(selection: $selectedItem,
                     matching: .images) {
            Label("Select a photo", systemImage: "photo")
        }
    }
}

matching 參數允許你指定要顯示的資源類型。在這裡,我們只選擇顯示圖像。在閉包中,我們使用 Label 視圖創建一個簡單的按鈕。

圖 53.1. 標準照片選擇器
圖 53.1. 標準照片選擇器

在選擇一張照片後,照片選取器會自動關閉,並將所選的照片項存儲在 selectedItem 變數中,該變數的類型是 PhotosPickerItem。要從項目中加載圖像,可以使用 loadTransferable(type:completionHandler:) 方法。你可以附加 onChange 修飾器來監聽 selectedItem 變數的更新。每當有變化時,你可以調用 loadTransferable 方法來加載資源數據,像這樣:

@State private var selectedImage: Image?

.
.
.

.onChange(of: selectedItem) { oldItem, newItem in
    Task {
        if let image = try? await newItem?.loadTransferable(type: Image.self) {
            selectedImage = image
        }
    }
}

在使用 loadTransferable 時,需要指定要檢索的資源類型。在這種情況下,我們使用 Image 類型來直接加載圖像。如果操作成功,該方法將返回一個 Image 視圖,可以直接用於將照片渲染到屏幕上。

if let selectedImage {
    selectedImage
        .resizable()
        .scaledToFit()
        .padding(.horizontal, 10)
}

實現內嵌的 PhotosPicker

現在你應該明白如何使用 PhotosPicker,讓我們看看如何將其嵌入到我們的演示應用程序中。我們將用內嵌的 Photos 選取器替換「選擇一張照片」按鈕。更新後的 PhotosPicker 版本帶有一個名為 photosPickerStyle 的新修飾符。通過指定 .inline 的值,Photos 選取器將自動嵌入應用程序中:

.photosPickerStyle(.inline)

你還可以附加標準的修飾器,如 .frame.padding,來調整選取器的大小。

圖 53.2. 內嵌照片選擇器範例
圖 53.2. 內嵌照片選擇器範例

預設情況下,選取器的頂部附件是導航欄,底部附件是工具欄。要禁用這兩個欄,你可以應用 photosPickerAccessoryVisibility 修飾器:

.photosPickerAccessoryVisibility(.hidden)

此外,你還可以選擇性地隱藏其中一個附件:

.photosPickerAccessoryVisibility(.hidden, edges: .bottom)

處理多個照片選擇

目前,Photos 選取器只允許用戶選擇單張照片。要啟用多個選擇,你可以通過將 selectionBehavior 設置為 .continuous.continuousAndOrdered 來選擇連續選擇行為:

PhotosPicker(selection: $selectedItems, 
                         maxSelectionCount: 5, 
             selectionBehavior: .continuousAndOrdered,
             matching: .images) {
    Label("Select a photo", systemImage: "photo")
}

如果你希望限制可選擇的項目數量,你可以使用 maxSelectionCount 參數來指定最大計數。

圖 53.3. 選擇多張照片
圖 53.3. 選擇多張照片

一旦使用者選擇了一組照片,它們將存儲在 selectedItems 數組中。selectedItems 數組已經被修改以容納多個項目,並且現在的類型是 PhotosPickerItem

@State private var selectedItems: [PhotosPickerItem] = []

要加載所選的照片,你可以像這樣更新 onChange 閉包:

.onChange(of: selectedItems) { oldItems, newItems in

    selectedImages.removeAll()

    newItems.forEach { newItem in

        Task {
            if let image = try? await newItem.loadTransferable(type: Image.self) {
                selectedImages.append(image)
            }
        }

    }
}

我使用了一個 Image 數組來存儲獲取的圖像。

@State private var selectedImages: [Image] = []

要顯示選擇的圖像,你可以使用水平滾動視圖(horizontal scroll view)。以下是可以放在 VStack 視圖的開頭的示例程式碼:

if selectedImages.isEmpty {
    ContentUnavailableView("No Photos", systemImage: "photo.on.rectangle", description: Text("To get started, select some photos below"))
        .frame(height: 300)
} else {

    ScrollView(.horizontal) {
        LazyHStack {
            ForEach(0..<selectedImages.count, id: \.self) { index in
                selectedImages[index]
                    .resizable()
                    .scaledToFill()
                    .frame(height: 250)
                    .clipShape(RoundedRectangle(cornerRadius: 25.0))
                    .padding(.horizontal, 20)
                    .containerRelativeFrame(.horizontal)
            }

        }
    }
    .frame(height: 300)
}

在 iOS 17 中,引入了一個名為 ContentUnavailableView 的新視圖。建議在無法顯示視圖的內容的情況下使用此視圖。因此,當沒有選擇照片時,我們使用 ContentUnavailableView 來呈現簡潔且信息豐富的消息。

圖 53.4. 使用 ContentUnavailableView 建立空內容視圖
圖 53.4. 使用 ContentUnavailableView 建立空內容視圖

總結

在 iOS 17 中,蘋果對原生的 Photos 選取器進行了改進。現在,你可以輕鬆地在你的 App 中包含它,而不需要使用單獨的表單。本教程解釋了更新的 PhotosPicker 視圖所帶來的新修飾器,並展示了如何創建內嵌的照片選取器。

參考資料,你可以在這裡下載示例項目: