[Swift UI]正方形のカメラViewでバーコードスキャンしてアラートで表示するまで

<img width="400" alt="IMG_5F82400E5BEF-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494487/f115274b-8ca1-3b1e-dbc8-d113d161645f.jpeg">

ちょっと長いですけどコピペで動きます


    import SwiftUI
    import AVFoundation

    struct ContentView2: View {
        
        @ObservedObject private var reader = Reader()
        
        var body: some View {
            VStack{
                
                //QR読み取りViewをセット：レイヤーにカメラプレビューのレイヤーをセット
                QRReaderView(caLayer: reader.videoPreviewLayer)
                //                .edgesIgnoringSafeArea(.all)
                //QRコードを読み込んだらAlertで読み取り結果を出す。読み取ったらAVCaptureSessionを停止しているのでOKボタンを押して再度読み取り開始
                    .alert(isPresented: $reader.isdetectQR) {
                        Alert(title: Text("バーコード読み取り成功"),
                              //                      message: Text("読み取りデータ：\(qrReader.qrData)"),dismissButton: .default(Text("OK"),action: {
                              message: Text("読み取りデータ：\(reader.ean13Data)"),dismissButton: .default(Text("OK"),action: {
                            
                            reader.startSession()
                        }))
                    }
            }
            .background(Color.white)
        }
    }

    struct QRReaderView: UIViewControllerRepresentable {
        var caLayer:CALayer
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<QRReaderView>) -> UIViewController {
            let viewController = UIViewController()
    //        viewController.view.backgroundColor = .orange
            viewController.view.layer.addSublayer(caLayer)
    //        ここでカメラのframe幅を変更する
            viewController.view.frame = CGRect(x: viewController.view.frame.height / 2, y: viewController.view.frame.width / 4, width: 300, height: 300)
            
            return viewController
        }
        
        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<QRReaderView>) {
            caLayer.frame = uiViewController.view.layer.frame
        }
    }

    class Reader: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject {
        
        @Published var qrData:String = ""
        @Published var ean13Data:String = ""
        
        @Published var isdetectQR = false
        @Published var isdetectean13 = false
        
        //カメラ用のAVsessionインスタンス作成
        private let AVsession = AVCaptureSession()
        //カメラ画像を表示するレイヤー
        var videoPreviewLayer:AVCaptureVideoPreviewLayer!
        // カメラの設定
        // 今回は背面カメラなのでposition: .back
        let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .front)
        
        override init() {
            super.init()
            cameraInit()
        }
        
        func cameraInit(){
            //カメラデバイスの取得
            let devices = discoverySession.devices
            
            //背面のカメラ情報を取得
            if let backCamera = devices.first {
                do {
                    //カメラ入力をinputとして取得
                    let input = try AVCaptureDeviceInput(device: backCamera)
                    
                    //Metadata情報（今回はQRコード）を取得する準備
                    //AVssessionにinputを追加:既に追加されている場合を考慮してemptyチェックをする
                    if AVsession.inputs.isEmpty {
                        AVsession.addInput(input)
                        //MetadataOutput型の出力用の箱を用意
                        let captureMetadataOutput = AVCaptureMetadataOutput()
                        //captureMetadataOutputに先ほど入力したinputのmetadataoutputを入れる
                        AVsession.addOutput(captureMetadataOutput)
                        //MetadataObjectsのdelegateに自己(self)をセット
                        captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
                        //Metadataの出力タイプをqrにセット
                        //                    captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
                        captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.ean13]
                        
                        //カメラ画像表示viewの準備とカメラの開始
                        //カメラ画像を表示するAVCaptureVideoPreviewLayer型のオブジェクトをsessionをAVsessionで初期化でプレビューレイヤを初期化
                        videoPreviewLayer = AVCaptureVideoPreviewLayer.init(session: AVsession)
                        videoPreviewLayer.videoGravity = .resizeAspectFill //←これ
                        //        previewLayer.connection?.videoOrientation = .landscapeRight
                        
                        //カメラ画像を表示するvideoPreviewLayerの大きさをview（superview）の大きさに設定
                        //                    videoPreviewLayer?.frame = previewLayer.bounds
                        //カメラ画像を表示するvideoPreviewLayerをビューに追加
                        //previewLayer.addSublayer(videoPreviewLayer!)
                    }
                    
                    
                    //セッションの開始(今回はカメラの開始)
                    DispatchQueue.global(qos: .background).async {
                        self.AVsession.startRunning()
                        
                    }
                } catch {
                    print("Error occured while creating video device input: \(error)")
                }
            }
        }
        
        
        func startSession() {
            if AVsession.isRunning { return }
            AVsession.startRunning()
        }
        
        func stopSession() {
            if !AVsession.isRunning { return }
            AVsession.stopRunning()
        }
        
        
    }
    //MARK: - AVCaptureMetadataOutputObjectsDelegate
    extension Reader:AVCaptureMetadataOutputObjectsDelegate{
        
        func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
            
            //カメラ画像にオブジェクトがあるか確認
            if metadataObjects.count == 0 {
                return
            }
            //オブジェクトの中身を確認
            for metadata in metadataObjects as! [AVMetadataMachineReadableCodeObject] {
                // metadataのtype： metadata.type
                print(metadata.type)
                print(metadata.stringValue!)
                
                // QRの中身： metadata.stringValue
                guard let data = metadata.stringValue else { return }
                isdetectQR = true
                //            isdetectean13 = true
                
                //            qrData = data
                ean13Data = data
                
                print("読み取りvalue：",data)
                //一旦て停止
                stopSession()
                //AVsession.stopRunning()
            }
        }
    }


https://qiita.com/celcior0913/items/6799f718b1ebccfc138b

このやり方で作ってたんですが、これだとアラートの表示とかできなくて別記事のバーコードスキャンのソースと上記で使った正方形カメラの表示のやり方を君合わせて一つのバーコード読み取り機能を実装しました

画面の一部にバーコードスキャンを導入したいなんて思ってる方はこちらのやり方とか参考にできる部分があるかなと思います

どなたかの参考になれば幸いです