今回は SwiftUI アプリで、フォトライブラリから画像を選択し、Image として表示する方法について紹介します。
SwiftUI 独自の機能としては現時点(2021/02/12現在)では提供されていないので、今回も例に漏れず、UIViewControllerRepresentable プロトコルを継承し、UIKit のUIImagePickerViewController をラップした View を作成します。
【SwiftUI】フォトライブラリから画像を取得する【ImagePicker】
UIViewControllerRepresentable の継承
先ずは、UIViewControllerRepresentable プロトコルを継承し以下のメソッドを実装します。
- makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController
- updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext)
updateUIViewController の方は今回は使用しませんが実装しないとエラーとなります。
struct ImagePicker: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
}
Coordinator内部クラス
続いてコアとなる内部クラス Coordinator を定義します。Coordinator には以下の2つのデリゲートを継承します。
- UIImagePickerControllerDelegate
- UINavigationControllerDelegate
また、親となる ImagePicker の参照を持たせます。
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
}
UIImagePickerController の初期化
続いて、makeUIViewController で初期化処理を行います。
var sourceType: UIImagePickerController.SourceType = .photoLibrary
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
UIImagePickerController を生成し、編集不可設定、読み込み元のタイプ、デリゲートを指定します。
sourceType は予め ImagePicker 本体に保持させたものを渡しています。
画像取得後の処理
フォトライブラリ画面で画像をタップして選択した後の処理ですが、先程継承した UIImagePickerControllerDelegate の以下のメソッドを実装します。
imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
}
このメソッドで画像を受け取り、表示元の画面へ反映する処理を行いたいと思います。
事前に、ImagePicker に選択画像のバインディング変数と、画面を閉じるための環境変数を定義しておきます。
@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) private var presentationMode
以下のように画像を取り出し、画面を閉じます。
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
ImagePicker 全体コード
ImagePicker 全体のコードとしては以下のようになります。
import UIKit
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
var sourceType: UIImagePickerController.SourceType = .photoLibrary
@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
ImagePicker の使用例
最後に、使用例として冒頭の動画のコードを紹介します。
struct ContentView: View {
@State private var image: UIImage?
@State var showingImagePicker = false
var body: some View {
VStack {
if let uiImage = image {
Image(uiImage: uiImage)
.resizable()
.frame(width: 200, height: 200)
.clipShape(Circle())
} else {
Image("noimage")
.resizable()
.frame(width: 200, height: 200)
.clipShape(Circle())
}
Spacer().frame(height: 32)
Button(action: {
showingImagePicker = true
}) {
Text("フォトライブラリから選択")
}
}
.sheet(isPresented: $showingImagePicker) {
ImagePicker(sourceType: .photoLibrary, selectedImage: $image)
}
}
}
ユーザー登録のあるアプリでプロフィール画像を選択する時などに使えそうですね。
なお、sourceType に .camera を指定するとカメラが立ち上がり撮影した画像を反映することができます。
ただし、事前に以下のように info.plist でカメラへのアクセス許可を確認するための設定を追加しておかないとクラッシュしてしまいますので注意してください。
以上
コメントを残す