美文网首页
Swift 版 二维码 扫码 iPad and iPhone

Swift 版 二维码 扫码 iPad and iPhone

作者: 小黑技术 | 来源:发表于2022-04-15 15:34 被阅读0次

    importUIKit

    importAVFoundation

    private let scanAnimationDuration = 3.0//扫描时长

    private let needSound = true //扫描结束是否需要播放声音

    private let scanWidth : CGFloat = 300 //扫描框宽度

    private let scanHeight : CGFloat = 300 //扫描框高度

    private let isRecoScanSize = true //是否仅识别框内

    private let scanBoxImagePath = "扫描框" //扫描框图片

    private let scanLineImagePath = "扫描线" //扫描线图片

    private let soundFilePath = "noticeMusic.caf" //声音文件

    class ZWScanQRCodeVCJoe: UIViewController{

        letcancelBtn=UIButton()//取消返回按钮

        var scanPane: UIImageView!///扫描框

        var scanPreviewLayer : AVCaptureVideoPreviewLayer! //预览图层

        var output : AVCaptureMetadataOutput!

        var scanSession:  AVCaptureSession?

        lazyvarscanLine:UIImageView= {

            letscanLine =UIImageView()

            scanLine.frame=CGRect(x:0,y:0,width:scanWidth,height:3)

            scanLine.image=UIImage(named:scanLineImagePath)

            returnscanLine

        }()

        override func viewDidLoad(){

            super.viewDidLoad()

            //初始化界面

            self.initView()

            //初始化ScanSession

            setupScanSession()

            // 监听屏幕旋转

            //        NotificationCenter.default.addObserver(self, selector: #selector(receiverNotification), name: UIDevice.orientationDidChangeNotification, object: nil)

            self.receiverNotification()

        }

        @objc func receiverNotification() {

            setLayerOrientationByDeviceOritation()

        }

        overridefuncviewWillAppear(_animated:Bool){

            super.viewWillAppear(animated)

            startScan()

        }

        //初始化界面

        funcinitView()  {

            //取消按钮 返回按钮

            cancelBtn.frame = CGRect(x: 63*WidthW, y: 70*WidthW, width: 92*WidthW, height: 92*WidthW)

            //        cancelBtn.backgroundColor = UIColor.gray

            cancelBtn.setTitleColor(UIColor.white, for: .normal)

            cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 18)

            cancelBtn.layer.cornerRadius = 3

            cancelBtn.clipsToBounds = true

            cancelBtn.tag=1

            cancelBtn.setImage(UIImage.init(named: "返回"), for: .normal)

            cancelBtn.addTarget(self, action: #selector(closeBtnClick), for: .touchUpInside)

            self.view.addSubview(cancelBtn)

            //

            scanPane=UIImageView()

            scanPane.frame=CGRect(x:300,y:100,width:400,height:400)

            scanPane.image = UIImage(named: scanBoxImagePath)

            self.view.addSubview(scanPane)

            //增加约束

            addConstraint()

            scanPane.addSubview(scanLine)

        }

        //返回页面

        @objc func closeBtnClick(){

            self.view.viewContainingController()?.navigationController?.popViewController(animated: false)

        }

        //扫描完成回调

        funcqrCodeCallBack(_codeString :String?) {

            self.confirm(title:"扫描结果",message: codeString,controller:self,handler: { (_)in

                //继续扫描

                self.startScan()

            })

        }

        func addConstraint() {

            scanPane.translatesAutoresizingMaskIntoConstraints = false

            //创建约束

            let widthConstraint = NSLayoutConstraint(item: scanPane as Any, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: scanWidth)

            let heightConstraint = NSLayoutConstraint(item: scanPane  as Any, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: scanHeight)

            let centerX = NSLayoutConstraint(item: scanPane as Any, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0)

            let centerY = NSLayoutConstraint(item: scanPane  as Any, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0)

            //添加多个约束

            view.addConstraints([widthConstraint,heightConstraint,centerX,centerY])

        }

        //初始化scanSession

        func setupScanSession(){

            do{

                //设置捕捉设备

                letdevice =AVCaptureDevice.default(for:AVMediaType.video)!

                //设置设备输入输出

                letinput =tryAVCaptureDeviceInput(device: device)

                letoutput =AVCaptureMetadataOutput()

                output.setMetadataObjectsDelegate(self,queue: DispatchQueue.main)

                self.output= output

                //设置会话

                let  scanSession =AVCaptureSession()

                scanSession.canSetSessionPreset(.high)

                ifscanSession.canAddInput(input){

                    scanSession.addInput(input)

                }

                ifscanSession.canAddOutput(output){

                    scanSession.addOutput(output)

                }

                //设置扫描类型(二维码和条形码)

                output.metadataObjectTypes= [

                    .qr,

                    .code39,

                    .code128,

                    .code39Mod43,

                    .ean13,

                    .ean8,

                    .code93

                ]

                //预览图层

                letscanPreviewLayer =AVCaptureVideoPreviewLayer(session:scanSession)

                scanPreviewLayer.videoGravity=AVLayerVideoGravity.resizeAspectFill

                scanPreviewLayer.frame=view.layer.bounds

                self.scanPreviewLayer= scanPreviewLayer

                setLayerOrientationByDeviceOritation()

                //保存会话

                self.scanSession= scanSession

            }catch{

                //摄像头不可用

                self.confirm(title: "温馨提示", message: "摄像头不可用", controller: self)

                return

            }

        }

        func setLayerOrientationByDeviceOritation() {

            if(scanPreviewLayer == nil){

                return

            }

            scanPreviewLayer.frame = view.layer.bounds

            view.layer.insertSublayer(scanPreviewLayer, at: 0)

            letscreenOrientation =UIDevice.current.orientation

            if(screenOrientation == .portrait){

                scanPreviewLayer.connection?.videoOrientation = .portrait

            }elseif(screenOrientation == .landscapeLeft){

                scanPreviewLayer.connection?.videoOrientation = .landscapeRight

            }elseif(screenOrientation == .landscapeRight){

                scanPreviewLayer.connection?.videoOrientation = .landscapeLeft

            }elseif(screenOrientation == .portraitUpsideDown){

                scanPreviewLayer.connection?.videoOrientation = .portraitUpsideDown

            }else{

                scanPreviewLayer.connection?.videoOrientation = .landscapeRight

            }

            //设置扫描区域

            NotificationCenter.default.addObserver(forName:NSNotification.Name.AVCaptureInputPortFormatDescriptionDidChange,object:nil,queue:nil,using: { (noti)in

                if(isRecoScanSize){

                    self.output.rectOfInterest = self.scanPreviewLayer.metadataOutputRectConverted(fromLayerRect: self.scanPane.frame)

                }else{

                    self.output.rectOfInterest=CGRect(x:0,y:0,width:1,height:1)

                }

            })

        }

        //设备旋转后重新布局

        override func viewDidLayoutSubviews() {

            super.viewDidLayoutSubviews()

            setLayerOrientationByDeviceOritation()

        }

        //开始扫描

        fileprivate func startScan(){

            scanLine.layer.add(scanAnimation(), forKey: "scan")

            guardletscanSession =scanSessionelse{return}

            if!scanSession.isRunning

            {

                scanSession.startRunning()

            }

        }

        //扫描动画

        private func scanAnimation() -> CABasicAnimation{

            letstartPoint =CGPoint(x:scanLine.center.x  ,y:1)

            letendPoint =CGPoint(x:scanLine.center.x,y:scanHeight-2)

            lettranslation =CABasicAnimation(keyPath:"position")

            translation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

            translation.fromValue=NSValue(cgPoint: startPoint)

            translation.toValue=NSValue(cgPoint: endPoint)

            translation.duration=scanAnimationDuration

            translation.repeatCount=MAXFLOAT

            translation.autoreverses=true

            returntranslation

        }

        //MARK: -

        //MARK: Dealloc

        deinit{

            ///移除通知

            NotificationCenter.default.removeObserver(self)

        }

    }

    //MARK: -

    //MARK: AVCaptureMetadataOutputObjects Delegate

    extension ZWScanQRCodeVCJoe : AVCaptureMetadataOutputObjectsDelegate

    {

        //捕捉扫描结果

        func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

            //停止扫描

            self.scanLine.layer.removeAllAnimations()

            self.scanSession!.stopRunning()

            //播放声音

            if(needSound){

                self.playAlertSound()

            }

            //扫描完成

            ifmetadataObjects.count>0{

                ifletresultObj = metadataObjects.firstas?AVMetadataMachineReadableCodeObject{

                    self.qrCodeCallBack(resultObj.stringValue)

                }

            }

        }

        //弹出确认框

        func confirm(title:String?,message:String?,controller:UIViewController,handler: ( (UIAlertAction) -> Swift.Void)? = nil){

            letalertVC =UIAlertController(title: title,message: message,preferredStyle: .alert)

            letentureAction =UIAlertAction(title:"确定",style: .destructive,handler: handler)

            alertVC.addAction(entureAction)

            controller.present(alertVC,animated:true,completion:nil)

        }

        //播放声音

        func playAlertSound(){

            guardletsoundPath =Bundle.main.path(forResource:soundFilePath,ofType:nilelse{return}

            guardletsoundUrl =NSURL(string: soundPath)else{return}

            varsoundID:SystemSoundID=0

            AudioServicesCreateSystemSoundID(soundUrl, &soundID)

            AudioServicesPlaySystemSound(soundID)

        }

    }

    相关文章

      网友评论

          本文标题:Swift 版 二维码 扫码 iPad and iPhone

          本文链接:https://www.haomeiwen.com/subject/pmcqertx.html