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を呼び出すことができました!
もっと良いやり方があったら教えてください!