SwiftUI 使用UIViewControllerRepresentable protocol存取相片
xcode 13.4.1, swift 5.5, iOS 15.4
2022-09-05
SwiftUI雖然已經提供了很多內建的元件,但SwiftUI還沒有UIKit成熟,像選擇相片在iOS 15是還沒有內建的(不過iOS 16就已提供),所以有些時候需要借助UIKit來實現,要做這個工作,就要通過SwiftUI提供的UIViewControllerRepresentable protocol來與UIKit中的視圖控制器 (View Controller)作橋樑溝通。
1. 創建一個符合UIViewControllerRepresentable protocol struct,這個就可以像其他內建的View來使用,這個struct主要要實作 makeUIViewController 和 updateUIViewController , makeUIViewController 主要是用來創建UIViewController , updateUIViewController 主要是用來更新 UIViewController,框架如下:
import UIKit
import SwiftUI
struct ImagePickerView : UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIImagePickerController {
// create UIImagePickerController
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
// update UIImagePickerController
}
}
2. 首先實作makeUIViewController,並反回一個 UIImagePickerController :
import UIKit
import SwiftUI
struct ImagePickerView : UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = UIImagePickerController.SourceType.photoLibrary
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
//update UIImagePickerController
}
}
3. 然後像其他內建的元件使用就可以,以下實作是按完圖片後,會彈出圖片選擇器,並選擇圖片,程式如下:
import SwiftUI
struct UIViewControllerRepresentableExample : View {
@State private var isShowImagePickerView : Bool = false
@State private var userPickedImage: Image? = Image("cat6")
var body: some View {
VStack {
userPickedImage!
.resizable()
.scaledToFit()
.onTapGesture {
isShowImagePickerView = true
}
}
.sheet(isPresented: $isShowImagePickerView) {
ImagePickerView()
}
}
}
4. 到目前為止已可以彈出圖片選擇器,但選完圖片後,App中顯示的圖片並沒有改變,為這把選中的圖片顯示在Image元件中,需要一個協調器,用這個協調器把得到選中的圖片並傳給Image元件,實現如下:
import UIKit
import SwiftUI
struct ImagePickerView : UIViewControllerRepresentable {
@Environment(\.presentationMode) var isPresented
@Binding var selectedImage: Image?
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = UIImagePickerController.SourceType.photoLibrary
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
//update UIImagePickerController
}
func makeCoordinator() -> Coordinator {
return Coordinator(picker: self)
}
}
class Coordinator : NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var picker: ImagePickerView
init(picker: ImagePickerView) {
self.picker = picker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let selectedUIImage = info[.originalImage] as? UIImage else { return }
self.picker.selectedImage = Image(uiImage: selectedUIImage)
self.picker.isPresented.wrappedValue.dismiss()
}
}
import SwiftUI
struct UIViewControllerRepresentableExample : View {
@State private var isShowImagePickerView : Bool = false
@State private var userPickedImage: Image? = Image("cat6")
var body: some View {
VStack {
userPickedImage!
.resizable()
.scaledToFit()
.onTapGesture {
isShowImagePickerView = true
}
}
.sheet(isPresented: $isShowImagePickerView) {
ImagePickerView(selectedImage: $userPickedImage)
}
}
}