关于AVFoundation的iOS 原生的QRCode扫描(swift3.0版本)(周五贴)

今天要介绍的是iOS7.0后AVFoundation框架提供的原生二维码扫描(非针对新手)
  1. 首先导入AVFoundation 框架。
  2. 我们要编写一个QRCode扫描管理对象MMBCaptureManager。它放出的delegate
    @objc protocol QISCaptureManagerDelegate : NSObjectProtocol {
    @objc optional func didChangeAccessCameraState(isGranted:Bool);
    @objc optional func didOutputDecodeStringValue(stringValue:NSString);
    @objc optional func didDecodeUnmatchType(codeType:NSString);
    }

    会把扫描结果以子线程的形式进行回调。

一开始需要请求相机的授权

接下来是一堆配置,生成AVCaptureSession,挂载output和input,videoPreViewLayer对象。AVCaptureDevice用设置是否自动对焦,闪光灯。在AVCaptureMetadataOutput里,我们设置metadata的类型为AVMetadataObjectTypeQRCode,值得一提的属性是AVCaptureMetadataOutput的rectOfInterest属性,在苹果的文档中,这个值以UV坐标的形式表示相机捕捉metaObject的区域。我这里是取 手机屏幕宽度-30*2 的 正方形区域,并把这个区域居中。
videoPreViewLayer则是渲染到屏幕的输出结果,是官方实现的比较便利把图像流输出到layer的方法,也可以自己设置OpenglContext去把流渲染,不过比较麻烦。

     // #pragma MARK: - 授权
    func  authCaputre()
    {

        let authStatus:AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)

        if(authStatus == .notDetermined)
        {
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: {
               granded  in

                if(granded){
                    self.setupCature()
                    self.didChangeAccessCameraState(isGranted: true);
                }
                else
                {
                    self.didChangeAccessCameraState(isGranted: false);
                }

            })
        }
        else if(authStatus == .authorized || authStatus == .restricted)
        {
            self.setupCature()
        }
        else if(authStatus == .denied)
        {
            (self.rootViewController as! MMBScanCodeViewController).alertCameraAuth()
        }

    }```




    // MARK: 开始捕捉
    func setupCature()
    {
        // MARK : 配置
        let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)!

        do
        {
          try captureDevice.lockForConfiguration()
        }
        catch { }

        //设置自动对焦
        if (captureDevice.isFocusModeSupported(.continuousAutoFocus))
        {

            captureDevice.focusPointOfInterest = CGPoint(x:0.5 , y:0.5)
            captureDevice.focusMode = .continuousAutoFocus

        }


        if(captureDevice.hasTorch && captureDevice.isTorchModeSupported(.auto))
        {
            captureDevice.torchMode = .auto
        }

        captureDevice.videoZoomFactor = captureDevice.activeFormat.videoZoomFactorUpscaleThreshold

        captureDevice.unlockForConfiguration()

        // MARK: 设置session
        var isHighPresent : Bool = false
        let idiom = UIDevice.current.userInterfaceIdiom
        if idiom == .pad || CGFloat(WindowHeight) < 480 + 1
        { isHighPresent = true}

        if isHighPresent {

           captureSession.sessionPreset = AVCaptureSessionPresetHigh
        }
        else if captureSession.canSetSessionPreset(AVCaptureSessionPreset1920x1080)
        {
            captureSession.sessionPreset = AVCaptureSessionPreset1920x1080
        }


        let captureInput = try? AVCaptureDeviceInput.init(device: captureDevice)

        if captureInput == nil { debugPrint("captureInput init falied!")
            return }

        if captureSession.canAddInput(captureInput)
        {
            captureSession.addInput(captureInput)
        }

        let captureOutput = AVCaptureMetadataOutput()
        if captureSession.canAddOutput(captureOutput)
        {
            captureSession.addOutput(captureOutput)
        }

        let queue  = DispatchQueue.init(label: "captureOutputQueue")
        captureOutput.setMetadataObjectsDelegate(self, queue: queue)


        captureOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]

        captureOutput.rectOfInterest = self.rectOfInterest()

        videoPreViewLayer = AVCaptureVideoPreviewLayer.init(session: self.captureSession)
        videoPreViewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill

    }

//我这里是这么计算rectOfInterest的
     rectOfInterest = CGRect(
                x: _cropRect!.origin.y / ScreenSize.height ,
                y: _cropRect!.origin.x /  ScreenSize.width,
                width: _cropRect!.size.height / ScreenSize.height ,
                height: _cropRect!.size.width /  ScreenSize.width
            )

3 到这里,设备就已经准备好了。但是设备还没启动呢!

   /** 开始捕捉*/
    func startReader()
    {
        self.isReading = true;
        if !captureSession.isRunning
        {
            self.captureSession.startRunning()
        }
    }

    /* 停止捕捉*/
    func stopReader()
    {
        self.isReading = false;
        if captureSession.isRunning
        {
            captureSession.stopRunning()
        }

    }
       /* 清楚资源*/
    func clearCaputure()
    {
        self.isReading = false

        self.stopReader()

        if captureSession.inputs.count > 0
        {
            captureSession.removeInput(captureSession.inputs[0] as! AVCaptureInput)
        }

        if captureSession.outputs.count > 0
        {
            captureSession.removeOutput(captureSession.outputs[0] as! AVCaptureOutput)
        }
    }

4 在Manager类的 deinit方法里添加self.clearCaputure

 deinit
    {
        self.clearCaputure()
    }

5 最重要的回调来了。检查自己的delegate是否是QRCode的对象类型,以2s一次将结果回调到主线程。

     // MARK : AVCaptureMetadataOutputObjectsDelegate
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
    {
        if !self.isReading { return};
        if metadataObjects != nil && metadataObjects.count > 0
        {
            let   metadataObject:AVMetadataMachineReadableCodeObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
            let   codeType:NSString = metadataObject.type as NSString
            let   stringValue:NSString = metadataObject.stringValue as NSString
            let   currentDate = Date()

            if  codeType.hasSuffix("QRCode") || codeType.hasSuffix("Code128")
            {

                if nextTipDate != nil //2s回调一次,避免频繁的回调
                {
                    if currentDate.compare(nextTipDate!) == .orderedAscending
                    {
                       return
                    }
                    else
                    {
                        self.nextTipDate = currentDate.addingTimeInterval(2.0)
                    }

                }
                else
                {
                    nextTipDate = currentDate.addingTimeInterval(2.0)
                }

                DispatchQueue.main.async {

                    if self.delegate != nil && (self.delegate?.responds(to: #selector(QISCaptureManagerDelegate.didOutputDecodeStringValue(stringValue:))))!
                    {
                       self.delegate!.didOutputDecodeStringValue!(stringValue: stringValue)

                    }
                }
                return
            }

            DispatchQueue.main.async {

                if self.delegate != nil && (self.delegate?.responds(to: #selector(QISCaptureManagerDelegate.didDecodeUnmatchType(codeType:))))!
                {
                    self.delegate!.didDecodeUnmatchType!(codeType: stringValue)

                }


            }

        }

    }

6 在ViewController中关于此Manager的使用 示例,需要注意的是使用授权

  var  captureManager:MMBCaptureManager = MMBCaptureManager.init(croRect: MMBQRcodeOverlayView.cropRect())
      captureManager.delegate = self
        captureManager.rootViewController = self



        self.addCapturePreviewSubLayer()

    func addCapturePreviewSubLayer()
    {
        if captureManager.videoPreViewLayer != nil
        {
        captureManager.videoPreViewLayer!.frame = self.view.layer.bounds
        self.view.layer.addSublayer(captureManager.videoPreViewLayer!)

        self.view.bringSubview(toFront: overlayView!)

            captureManager.startReader()

        }
    }


    // MARK:  QISCaptureManagerDelegate
    func didChangeAccessCameraState(isGranted: Bool) {

        if isGranted {
            self.addCapturePreviewSubLayer()
        }
        else{
            self.alertCameraAuth()
        }
    }

    func didOutputDecodeStringValue(stringValue: NSString) {

        debugPrint("scan result -- \(stringValue)")
    }

好了,我会把自己相关ViewConroller和ScanView和此类上传到github,有需要的同学拿去用吧。祝安!!

`https://github.com/WeipengChan/ScanCodeDemo 完整demo github地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值