투케이2K

403. (ios/swift5) [유틸 파일] observable_Qr_Barcode_Scan : QR , Barcode 스캔 수행 실시 - AVCaptureDevice 본문

IOS

403. (ios/swift5) [유틸 파일] observable_Qr_Barcode_Scan : QR , Barcode 스캔 수행 실시 - AVCaptureDevice

투케이2K 2023. 12. 21. 19:56

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : SWIFT5

 

[소스 코드]

 
// -----------------------------------------------------------------------------------------
// MARK: - [전역 변수 선언]
// -----------------------------------------------------------------------------------------
private let ACTIVITY_NAME = "C_Ui_View"

var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
let scanOperationQueue = OperationQueue()
var scanFlag = false
var scanResult = ""





// MARK: - [extension 정의 실시 : 뷰 컨트롤러]
extension UIViewController: AVCaptureMetadataOutputObjectsDelegate {

    // -----------------------------------------------------------------------------------------
    // MARK: - [QR 및 Barcode 스캔 팝업창 호출 메소드]
    // -----------------------------------------------------------------------------------------
    func observable_Qr_Barcode_Scan(title:String, okBtn:String, callback: @escaping (String) -> ()) {
        
        /*
        // -----------------------------------------
        [observable_Qr_Barcode_Scan 메소드 설명]
        // -----------------------------------------
        1. QR 및 Barcode 스캔 팝업창 호출 메소드
        // -----------------------------------------
        2. info plist 설정 :

        Privacy - Camera Usage Description
        // -----------------------------------------
        3. 필요 import 설정 :

        import AVKit
        // -----------------------------------------
        4. 호출 방법 :
         
         self.observable_Qr_Barcode_Scan(title: "Qr 및 Barcode 스캔", okBtn:"닫기"){(result) in
             S_Log._D_(description: "Qr 및 Barcode 스캔 콜백 결과 확인", data: ["\(result)"])
             
             // [팝업창 표시]
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                 
                 if C_Util().stringNotNull(str: "\(result)") == true {
                     self.showAlert(
                         type: 0,
                         tittle: "Qr 및 Barcode 스캔 콜백 결과 확인",
                         content: "\(result)",
                         okBtb: "확인",
                         noBtn: ""
                     )
                 }
                 
             }

         }
         
        // -----------------------------------------
        */

        // [작업 큐에 추가]
        scanResult = ""

        scanOperationQueue.isSuspended = true
        let block = { callback(scanResult) } // [콜백 데이터]
        scanOperationQueue.addOperation(block)

        
        // [메인 큐에서 비동기 방식 실행 : UI 동작 실시]
        DispatchQueue.main.async {
            S_Log._D_(description: "QR 및 Barcode 스캔 팝업창 호출 수행 실시", data: [
                "tittle :: \(title)"
            ])


            // [변수 초기화]
            scanFlag = false


            // [UIAlertController 생성]
            let alert = UIAlertController(
                title: title,
                message: "",
                preferredStyle: .alert
            )
            
            
            // [닫기 버튼 등록 실시]
            if C_Util().stringNotNull(str: okBtn) == true {
                let okAction = UIAlertAction(title: okBtn, style: .default) { (action) in
                    
                    // [스캔 중지]
                    if captureSession != nil {
                        captureSession?.stopRunning() // [스캔 종료]
                    }
                    
                    // [콜백 반환]
                    scanOperationQueue.isSuspended = false
                    
                    // [버튼 클릭 이벤트 내용 정의 실시]
                    return
                }
                // [버튼 클릭 이벤트 객체 연결]
                alert.addAction(okAction)
            }


            // [AVCaptureDevice 생성]
            let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
            if let captureDevice = captureDevice {
                do {
                    captureSession = AVCaptureSession()
                    
                    let input: AVCaptureDeviceInput
                    input = try AVCaptureDeviceInput(device: captureDevice)
                    captureSession?.addInput(input)
                    
                    
                    let metadataOutput = AVCaptureMetadataOutput()
                    captureSession?.addOutput(metadataOutput) // [결과 출력 지정]
                    metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
                    metadataOutput.metadataObjectTypes = [.qr, .code128] // MARK: [스캔 형식 지정]
                    
                    
                    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
                    videoPreviewLayer?.videoGravity = .resizeAspectFill
                    
                    let uiView = UIView()
                    uiView.frame = CGRect(x: alert.view.frame.width/15, y: alert.view.frame.width/5, width: alert.view.frame.width-150, height: alert.view.frame.width-150)
                    
                    
                    videoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: uiView.frame.width, height: uiView.frame.height)
                    uiView.layer.addSublayer(videoPreviewLayer!)

                    alert.view.addSubview(uiView) // [팝업창에 추가]

                    // [Alert LayoutConstraint]
                    let height = NSLayoutConstraint(item: alert.view!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: alert.view.frame.width)
                    let width = NSLayoutConstraint(item: alert.view!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: alert.view.frame.width-100)
                    alert.view.addConstraint(height)
                    alert.view.addConstraint(width)
                    
                    DispatchQueue.global().async {
                        captureSession?.startRunning() // [스캔 시작]
                    }
                    
                } catch {
                    S_Log._D_(description: "QR 및 Barcode 스캔 팝업창 에러 발생", data: [
                        "Exception :: \(error.localizedDescription)"
                    ])

                    alert.message = "[Exception] : \(error.localizedDescription)"
                }
            }
            else {
                S_Log._D_(description: "QR 및 Barcode 스캔 팝업창 에러 발생", data: [
                    "Error :: AVCaptureDevice Is Nil"
                ])

                alert.message = "[Error] : AVCaptureDevice Is Nil"
            }
            
            
            // [alert 팝업창 활성 실시]
            self.present(alert, animated: false, completion: nil)
            
        }
    }





    // -----------------------------------------------------------------------------------------
    // [AVCaptureMetadataOutputObjectsDelegate : Delegate] : 카메라 스캔 콜백
    // -----------------------------------------------------------------------------------------
    public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        
        DispatchQueue.main.async {

            if scanFlag == false {

                // [스캔 플래그 값 변경]
                scanFlag = true


                // [스캔 결과 없음]
                if metadataObjects.count == 0 {
                    scanFlag = false // [스캔 플래그 값 변경]
                    return
                }
                
                // [스캔 데이터 확인]
                let metaDataObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
                guard let StringCodeValue = metaDataObject.stringValue else {
                    scanFlag = false // [스캔 플래그 값 변경]
                    return
                }
                guard let _ = videoPreviewLayer?.transformedMetadataObject(for: metaDataObject) else {
                    scanFlag = false // [스캔 플래그 값 변경]
                    return
                }


                // [널 체크 수행]
                if C_Util().stringNotNull(str: StringCodeValue) == true { // [널 아님]

                    // [스캔 결과 확인]
                    S_Log._D_(description: "QR 및 Barcode 스캔 결과 확인", data: [
                        "\(StringCodeValue)"
                    ])
                    scanResult = "\(StringCodeValue)"


                    // [결과 표시 Alert 팝업창 표시]
                    let scan = UIAlertController(
                        title: "QR 및 Barcode 스캔 결과",
                        message: "\(scanResult)",
                        preferredStyle: .alert
                    )


                    // [스캔 관련 객체 초기화]
                    DispatchQueue.global().async {
                        captureSession?.stopRunning() // [스캔 종료]
                    }
                    
                    
                    // [스캔창 팝업창 닫기]
                    self.dismiss(animated: false)


                    // [콜백 반환]
                    scanOperationQueue.isSuspended = false

                }
                else {
                    S_Log._D_(description: "QR 및 Barcode 스캔 결과 : Data Is Null", data: nil)
                }

            }

        }
        
    }
    
    
} // [extension 종료]
 

[결과 출력]

 

반응형
Comments