在 SwiftUI 中實作呼叫系統相冊
0. 專案需求
許多 app 會要求用戶上傳照片到服務器或者 app 中進行后續處理,就會需要從設備相冊中選取照片,這是一個相當普遍的需求,最終實作效果如下,
在 Xcode 預覽模式下打開 live view 可以實時預覽組件的運行情況,這里我們定義了一個選取照片按鈕,而下方是一個 Image View,不過由于剛開始的時候沒有選取任何照片,所以不顯示任何內容,

當點擊該按鈕時,將會調出照片選取頁面,也就是 Picker,

這個 Picker 可以訪問到設備(這里是 Simulator)的圖庫相冊,由于 iOS 的使用習慣,大部分人會把大部分照片直接存在圖庫中,而不是存盤到 “檔案” 中,所以可以認為選取設備的所有照片,Picker 下方長條的按鈕可以在 Picker 內在線預覽所選中的照片,預覽時支持放大、旋轉等操作,
當選擇完成后點擊 “Add” 后,選取的照片加入到程式中,此時下方隱藏的 Image View 將顯示選取的照片,

這就是這個組件所用到的功能,接下來介紹實作這個組件使用到的技術和實作方法,
1. 使用技術
1.1 SwiftUI
SwiftUI 是蘋果在2019年新推出的 Swift 原生 UI 框架,通過這個 UI 框架,開發者可以輕松實作美觀大氣的界面設計并且在全平臺(iOS、iPadOS、macOS、tvOS、watchOS)上獲得相同且美觀的頁面,比如,可以實作不同尺寸螢屏自適應布局,自動實作黑夜模式等等,同時,在開發程序中,Xcode 針對 SwiftUI 還提供了預覽功能,可以實時顯示界面設計效果,也可以在預覽區域選取控制元件,左邊的代碼區域也會自動選中并自動修改屬性,目前還在發展的階段,不斷有新的、更易用的 API 面世,這兩年 Apple 在下一波大棋,把所有平臺的軟硬體都打通,這個 SwiftUI 也是實作這個愿景的一個工具,Apple 官方的應用和一些精品應用已經從 UIKit 轉移到了 SwiftUI,這應該是一個趨勢,就像 iOS 開發會從 object-C 遷移到 Swift 一樣,
1.2 PhotoKit & PhotosUI
PhotoKit 是蘋果近年來提出用于執行和照片相關功能的現代 API,PhotosUI 是其中的一個框架,可以實作比如圖片選擇等功能,下面是 Apple 對于 PhotoKit 的說明,

PhotoKit 這幾年功能不斷完善,其中 PhotoUI 的各個組件由于美觀的外觀和異步執行、保護用戶隱私的特性,逐步在取代原先 UIKit 中有關圖片的組件,比如這篇文章介紹的 PHPickerViewController 就是 UIImagePickerViewController 的現代替代,
2. 代碼
2.1 PhotoPicker 圖庫選取器的撰寫
在 SwiftUI 中,與其他框架或語言常見的用類定義不同,是用結構體定義的,不得不說,Swift 語言的結構體的功能很強大,
struct PhotoPicker: UIViewControllerRepresentable {
}
這里要提到在 SwiftUI 中集合 UIKit 功能的方法,就是在實作結構體時要實作 UIViewControllerRepresentable 協議(這里的協議可以理解為類似于介面一樣的東西),這個協議可以幫助 SwiftUI 實作一個 UIKit 中 UIViewController 組件的組件,
要實作協議,根據檔案,我們就要實作如下方法和類,


下面就開始實作各個方法和類,不過,在此之前,要定義幾個需要的成員變數,
let configuration: PHPickerConfiguration
@Binding var pickerResult: [UIImage]
@Binding var isPresented: Bool
這里定義了三個變數,第一個是對 PHPickerViewController 的配置,第二個和第三個分別是選取結果和是否顯示圖庫選取器界面,注意后兩個變數前的修飾符 @Binding,代表這兩個變數與外部的變數系結,由 SwiftUI 管理,當外部變數更新時或內部變數發生變化時,可以實時顯示變化,
接著實作協議中規定的兩個方法,
func makeUIViewController(context: Context) -> PHPickerViewController {
let controller = PHPickerViewController(configuration: configuration)
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { }
第一個方法是繪制 UIViewController 組件,這里我們要繪制一個 PHPickerViewController,所以回傳值是這個組件,方法體內創建了一個組件,并且將該組件的委托交給背景關系的協調器,最后回傳這個建立的組件,
第二個方法是更新 UIViewController 組件,這里我們不需要更新組件,就將方法體空著就好,
然后給用戶提供一個協調物件,要實作 makeCoordinator 方法,并定義一個 Coordinator 類,請看如下代碼:
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: PHPickerViewControllerDelegate {
private let parent: PhotoPicker
init(_ parent: PhotoPicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
for image in results {
if image.itemProvider.canLoadObject(ofClass: UIImage.self) {
image.itemProvider.loadObject(ofClass: UIImage.self) {
(newImage, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.parent.pickerResult.append(newImage as! UIImage)
}
}
} else {
print("Loaded Asset is not a Image")
}
}
parent.isPresented = false
}
}
首先是 makeCoordinator 方法,創建一個協調器,并系結呼叫的父類物件,這里要將協調的父類設為這個結構體(PhotoPicker),所以要寫為 Coordinator(self),
然后我們要定義一個 Coordinator 類,這個類要負責和 ViewController 與其他 SwiftUI 協調,在委托模式中,這個類當然要設定為 PHPickerViewControllerDelegate,
這個類里有一個變數 parent,負責指定委托的父物件,這里的父物件是結構體 PhotoPicker,所以我們這樣定義變數并且在初始化類時必須要傳入一個有效的 PhotoPicker 物件,
在 Apple Developer 查詢 PHPickerViewControllerDelegate 的檔案,要實作 picker 方法去定義當用戶在選擇器在選擇好以后或者點擊取消(dismiss)后要執行的動作,通過查詢檔案,isPresented 變數是 SwiftUI 定義的一個變數,用來指示現在是否呈現擁有這個環境的組件是否呈現,
我們看 picker 方法,
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
for image in results {
if image.itemProvider.canLoadObject(ofClass: UIImage.self) {
image.itemProvider.loadObject(ofClass: UIImage.self) {
(newImage, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.parent.pickerResult.append(newImage as! UIImage)
}
}
} else {
print("Loaded Asset is not a Image")
}
}
parent.isPresented = false
}
}
當點擊成功或者取消時,都會回傳一個 results,我們每次都需要處理這個 results,這里我們處理結果,并將結果中可讀取的物件添加到 pickerResult 中,最后我們將父類的 isPresented 設定為 false,這樣就能在執行完操作以后自動將圖庫選擇器關閉,這里我們不需要對 UI 影片進行任何設定,所有影片都會由 SwiftUI 自動渲染,(不會有人覺得自己做影片能做過 Apple 吧…)
通過上邊的代碼,我們就成功定義了一個圖庫選擇器,下面在 SwiftUI 中對該 PhotoPicker 進行測驗,
2.2 測驗 PhotoPicker
寫一個按鈕測驗 PhotoPicker 的功能,語法都是 SwiftUI 的基本語法,代碼如下,
struct PhotoPickerDemo: View {
@State private var isPresented: Bool = false
@State var pickerResult: [UIImage] = []
var config: PHPickerConfiguration {
var config = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
config.filter = .images
config.selectionLimit = 0
return config
}
var body: some View {
ScrollView {
LazyVStack {
Spacer()
Button(action: { isPresented.toggle()}) {
Text("選取照片")
.font(.largeTitle)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(15)
.shadow(radius: 12)
}
.sheet(isPresented: $isPresented) {
PhotoPicker(configuration: self.config, pickerResult: $pickerResult, isPresented: $isPresented)
}
ForEach(pickerResult, id: \.self) {
image in
Image.init(uiImage: image)
.resizable()
.frame(alignment: .center)
.aspectRatio(contentMode: .fit)
}
}
}
}
}
就可以實作上面展示的效果了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/304971.html
標籤:其他
