SwiftUI と組み合わせて、UIImagePickerController を使う方法を説明します。

Sponsor Link
目次
Info.plist
最近の iOS アプリのお約束ですが、ユーザーデータ等にアクセスするためには、Info.plist にその理由を記載する必要があります。
カメラにアクセスするならば、以下のキー値とその理由を、Info.plist に追加しておく必要があります。
- NSCameraUsageDescription
- カメラを使う理由を記載
写真アルバムにアクセスするならば、NSPhotoLibraryAddUsageDescription も追加する必要があります。
UIViewControllerRepresentable
UIKit の ViewController を SwiftUI に組み込むときには、UIViewControllerRepresentable を使って、wrap します。
UIImagePickerController も UIViewControllerRepresentable を使って、以下のように wrap することができます。
@Binding の image はユーザーが、写真を撮ったり、選択したときに、返される UIImage です。showCameraView は、カメラ/写真ビューの表示制御フラグです。
内部クラス Controller を作って、Delegate に設定しています。ユーザー操作に応答するための機能を実装します。UIImagePickerControllerDelegate 相当です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
struct SwiftUIImagePicker: UIViewControllerRepresentable { @Binding var image: UIImage? @Binding var showCameraView: Bool func makeCoordinator() -> Coordinator { return Coordinator(self) } func makeUIViewController(context: Context) -> UIImagePickerController { let viewController = UIImagePickerController() viewController.delegate = context.coordinator return viewController } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { print("updateUIViewController is called") } class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { let parent: SwiftUIImagePicker init(_ parent: SwiftUIImagePicker) { self.parent = parent } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { // user picked an image if let uiImage = info[.originalImage] as? UIImage { self.parent.image = uiImage } self.parent.showCameraView = false } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { // canceled self.parent.showCameraView = false } } } |
UIImagePickerControllerDelegate
ユーザーの操作に対して呼び出されるDelegateが、UIImagePickerControllerDelegate です。
以下の関数がポイントになります。
- func imagePickerController(UIImagePickerController, didFinishPickingMediaWithInfo: [UIImagePickerController.InfoKey : Any])
- ユーザーが撮影した写真に対して”これを使う”を選択したり、写真アプリから写真を選択すると呼び出されます。
- func imagePickerControllerDidCancel(UIImagePickerController)
- ユーザーが、キャンセルを選択すると呼び出されます。
例えば、ユーザーが写真を撮影したり、選択したときに、UIImage として保存するコードは以下のようになります。
1 2 3 4 5 6 7 8 |
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let uiImage = info[.originalImage] as? UIImage { // イメージを選択された? self.parent.image = uiImage // 選択されたら、内部変数に保存 } self.parent.showCameraView = false // 選択後、元の画面に戻る } |
サンプルアプリ
仕様
画面のカメラボタンを押すと、カメラでの撮影ビューを開きます。
写真が撮影されたら、元の画面のカメラボタンの位置に、撮影された写真を表示します。
起動直後 (スナップショットは、iPad の Landscape ですが、特に意味はありません)
カメラボタン押下後
ここで、キャンセルが押されると Delegate の該当コールバック(DidCancel)が呼ばれます。撮影ボタンを押しても、Delegate は、何も呼び出されません。(UIImagePickerController の内部遷移が起こっています)
撮影ボタン押下後
ここで、”Use Photo”ボタンが押されると、Delegate の該当コールバックが呼ばれます(didFinishPickngMediaWithInfo)。”Retake”ボタンは、再度撮影画面に戻りますが、Delegate は、何も呼び出されません。(UIImagePickerController の内部遷移が起こっています)
”Use Photo”ボタン押下後
選択された写真が、中央に表示されます。
サンプルの実装コードは以下のものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// // SwiftUIImagePicker.swift // CameraAccess // // Created by Tomoaki Yagishita on 2020/08/03. // Copyright © 2020 SmallDeskSoftware. All rights reserved. // import Foundation import SwiftUI struct SwiftUIImagePicker: UIViewControllerRepresentable { @Binding var image: UIImage? @Binding var showCameraView: Bool func makeCoordinator() -> Coordinator { return Coordinator(self) } func makeUIViewController(context: Context) -> UIImagePickerController { let viewController = UIImagePickerController() viewController.delegate = context.coordinator if UIImagePickerController.isSourceTypeAvailable(.camera) { viewController.sourceType = .camera } return viewController } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { print("updateUIViewController is called") } class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { let parent: SwiftUIImagePicker init(_ parent: SwiftUIImagePicker) { self.parent = parent } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { // user picked an image if let uiImage = info[.originalImage] as? UIImage { self.parent.image = uiImage } self.parent.showCameraView = false } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { self.parent.showCameraView = false } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// // ContentView.swift // CameraAccess // // Created by Tomoaki Yagishita on 2020/08/03. // Copyright © 2020 SmallDeskSoftware. All rights reserved. // import SwiftUI struct ContentView: View { @State private var image: UIImage? @State private var showCameraView = false var body: some View { VStack { if image != nil { Image(uiImage: image!) .resizable() .scaledToFit() } else { Button(action: { showCameraView.toggle() }, label: { Image(systemName: "camera") .resizable() .scaledToFit() .frame(width: 300, height: 300) }) } Text("here is the place for options") .padding() } .sheet(isPresented: $showCameraView, content: { SwiftUIImagePicker(image: self.$image, showCameraView: self.$showCameraView) }) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } |
まとめ: UIImagePickerControllerの使い方
カメラビューを起動して、画像を選択するだけであれば、UIImagePickerController を使うのが非常に簡単です。SwiftUI からでも上記のように非常に簡単に取得することができます。
作ったSwiftUIImagePickerControllerは、こちらから。
説明は以上です。
Sponsor Link