SwiftUI と組み合わせて、UIImagePickerController を使う方法を説明します。
iOS14 以降では、UIImagePickerController の代わりに、PHPickerViewController の使用を勧められています
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 相当です。
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 として保存するコードは以下のようになります。
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”ボタン押下後
選択された写真が、中央に表示されます。
サンプルの実装コードは以下のものです。
//
// 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
}
}
}
//
// 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は、こちらから。
説明は以上です。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
# SwiftUI2.0 が登場したことで少し古くなってしまいましたが、いまでも 定番本です。
Sponsor Link