SwiftUIでカメラを扱いたくなって色々調べた&試したので記事にします。
SwiftUIでカメラを扱いたい!
SwiftUIでカメラを直接扱うことは現在のところできず、UIKitで作ったカメラのビューを表示する必要があります。 カメラを使う一番簡単な方法は、UIImagePickerControllerを使うことです。これは画像を扱う一番簡単な方法で、カメラやメディアにアクセスするためのUIを持っています。使う側はDelegate経由で画像を受け取ることができます。
これはUIViewControllerのサブクラスなので、UIViewControllerRepresentableのサブクラスを定義し、UIImagePickerControllerを返すことでSwiftUIで扱えるようになると思ったので、以下のようなコードで試してみました。
struct ImagePickerWrapper : UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<CardCaptureViewControllerWrapper>) -> CardCaptureViewControllerWrapper.UIViewControllerType {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.modalPresentationStyle = .fullScreen
return imagePicker
}
func updateUIViewController(_ uiViewController: CardCaptureViewControllerWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<CardCaptureViewControllerWrapper>) {
}
}
一見これで動作しているようですが、UIViewControllerRepresentableを経由したせいか、横画面で崩れてしまいました。

また、この方法ではフルスクリーンのモーダルにすることができず、UIKitでUIImagePickerControllerを使った場合とは少し表示が変わってしまいます。
SwiftUIでView Controllerにアクセスする
UIViewControllerRepresentableを使う方法だとうまくいかなかったので、SwiftUIからUKitに直接アクセスする方法で解決しました。以下のように、独自のEnvironmentを定義し、任意のSwiftUIのViewからView Controllerを取得できるようにします。
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: UIViewController? {
return UIApplication.shared.windows.first?.rootViewController
}
}
extension EnvironmentValues {
var viewController: UIViewController? {
get { return self[ViewControllerKey.self] }
set { self[ViewControllerKey.self] = newValue }
}
}
これを定義しておくことで、任意のViewからView Controllerにアクセスできます。
struct ContentView: View {
@Environment(\.viewController)
var viewController
var body: some View {
return Text("Hello World!")
}
}
あとは、ボタンなどでUIImagePickerControllerのインスタンスを生成し、表示すればカメラを使うことができます。
struct ContentView: View {
@Environment(\.viewController)
var viewController
var body: some View {
return Text("Hello World!")
}
}

これでSwiftUIからUIImagePickerControllerを呼び出すことができました!
もっと良いやり方があったら教えてください!