美文网首页
摄像头抓捕二维码

摄像头抓捕二维码

作者: 沁晓Chr | 来源:发表于2015-12-22 18:01 被阅读140次

    摄像头抓捕二维码

    AVCaptureDeviceInput,AVCaptureSession,AVCaptureMetadataOutput,AVCaptureVideoPreviewLayer

    AVMetadataMachineReadableCodeObject,AVMetadataObject

    相关类
    
        /// 输入对象
        private lazy var input: AVCaptureDeviceInput? = {
            let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
            return try? AVCaptureDeviceInput(device: device)
        }()
        
        /// 会话
        private lazy var session: AVCaptureSession = AVCaptureSession()
        
        /// 输出对象
        private lazy var output: AVCaptureMetadataOutput = {
           let out = AVCaptureMetadataOutput()
            // 设置输出对象解析数据时感兴趣的范围
            // 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)全屏比例算
            // 通过对这个值的观察, 我们发现传入的是比例
            // 注意: 参照是以横屏(竖屏逆时针90度,Landscape Left)的左上角作为, 而不是以竖屏
    //        out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
            
            // 1.获取屏幕的frame
            let viewRect = self.view.frame
            // 2.获取扫描容器的frame
            let containerRect = self.customContainerView.frame
            let x = containerRect.origin.y / viewRect.height;
            let y = containerRect.origin.x / viewRect.width;
            let width = containerRect.height / viewRect.height;
            let height = containerRect.width / viewRect.width;
            
             out.rectOfInterest = CGRect(x: x, y: y, width: width, height: height)
            
            return out
        }()
        
        /// 预览图层
        private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
        
        /// 专门用于保存描边的图层
        private lazy var containerLayer: CALayer = CALayer()
    }
    
    
            // 1.判断输入能否添加到会话中
           
            // 2.判断输出能够添加到会话中
            
            // 3.添加输入和输出到会话中
            
            // 4.设置输出能够解析的数据类型
            
            // 注意点: 设置数据类型一定要在输出对象添加到会话之后才能设置
            
            // 5.设置监听监听输出解析到的数据
           
            // 6.添加预览图层
            
            // 7.添加容器图层,专门用于保存描边的图层
            
            // 8.开始扫描
    
    //  QRCodeViewController.swift
    
    import UIKit
    import AVFoundation
    
    class QRCodeViewController: UIViewController {
        
        /// 扫描容器
        @IBOutlet weak var customContainerView: UIView!
        /// 底部工具条
        @IBOutlet weak var customTabbar: UITabBar!
        /// 结果文本
        @IBOutlet weak var customLabel: UILabel!
       /// 容器视图高度约束
        @IBOutlet weak var containerHeightCons: NSLayoutConstraint!
    
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // 1.设置默认选中
            customTabbar.selectedItem = customTabbar.items?.first
            
            // 2.添加监听, 监听底部工具条点击
            customTabbar.delegate = self
            
            // 3.开始扫描二维码
            scanQRCode()
        }
        
        override func viewDidAppear(animated: Bool) {
            super.viewDidAppear(animated)
            
            startAnimation()
        }
        
        // MARK: - 内部控制方法
        private func scanQRCode()
        {
            // 1.判断输入能否添加到会话中
            if !session.canAddInput(input)
            {
                return
            }
            // 2.判断输出能够添加到会话中
            if !session.canAddOutput(output)
            {
                return
            }
            // 3.添加输入和输出到会话中
            session.addInput(input)
            session.addOutput(output)
            
            // 4.设置输出能够解析的数据类型
            // 注意点: 设置数据类型一定要在输出对象添加到会话之后才能设置
            output.metadataObjectTypes = output.availableMetadataObjectTypes
            
            // 5.设置监听监听输出解析到的数据
            output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
            
            // 6.添加预览图层
            view.layer.insertSublayer(previewLayer, atIndex: 0)
            previewLayer.frame = view.bounds
            
            // 7.添加容器图层,专门用于保存描边的图层
            view.layer.addSublayer(containerLayer)
            containerLayer.frame = view.bounds
            
            // 8.开始扫描
            session.startRunning()
            
        }
        
    
        }
        
        @IBAction func photoBtnClick(sender: AnyObject) {
        }
        @IBAction func closeBtnClick(sender: AnyObject) {
            dismissViewControllerAnimated(true, completion: nil)
        }
        
        // MARK: - 懒加载
        /// 输入对象
        private lazy var input: AVCaptureDeviceInput? = {
            let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
            return try? AVCaptureDeviceInput(device: device)
        }()
        
        /// 会话
        private lazy var session: AVCaptureSession = AVCaptureSession()
        
        /// 输出对象
        private lazy var output: AVCaptureMetadataOutput = {
           let out = AVCaptureMetadataOutput()
            // 设置输出对象解析数据时感兴趣的范围
            // 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)全屏比例算
            // 通过对这个值的观察, 我们发现传入的是比例
            // 注意: 参照是以横屏(竖屏逆时针90度,Landscape Left)的左上角作为, 而不是以竖屏
    //        out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
            
            // 1.获取屏幕的frame
            let viewRect = self.view.frame
            // 2.获取扫描容器的frame
            let containerRect = self.customContainerView.frame
            let x = containerRect.origin.y / viewRect.height;
            let y = containerRect.origin.x / viewRect.width;
            let width = containerRect.height / viewRect.height;
            let height = containerRect.width / viewRect.width;
            
             out.rectOfInterest = CGRect(x: x, y: y, width: width, height: height)
            
            return out
        }()
        
        /// 预览图层
        private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
        
        /// 专门用于保存描边的图层
        private lazy var containerLayer: CALayer = CALayer()
    }
    
    extension QRCodeViewController: AVCaptureMetadataOutputObjectsDelegate
    {
        /// 只要扫描到结果就会调用
        func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!)
        {
            // 1.显示结果
            customLabel.text =  metadataObjects.last?.stringValue
            
            clearLayers()
            
            // 2.拿到扫描到的数据
            guard let metadata = metadataObjects.last as? AVMetadataObject else
            {
                return
            }
            // 通过预览图层将corners值转换为我们能识别的类型
            let objc = previewLayer.transformedMetadataObjectForMetadataObject(metadata)
            // 2.对扫描到的二维码进行描边
            drawLines(objc as! AVMetadataMachineReadableCodeObject)
        }
        
        /// 绘制描边
        private func drawLines(objc: AVMetadataMachineReadableCodeObject)
        {
            
            // 0.安全校验
            guard let array = objc.corners else
            {
                return
            }
            
            // 1.创建图层, 用于保存绘制的矩形
            let layer = CAShapeLayer()
            layer.lineWidth = 2
            layer.strokeColor = UIColor.greenColor().CGColor
            layer.fillColor = UIColor.clearColor().CGColor
            
            // 2.创建UIBezierPath, 绘制矩形
            let path = UIBezierPath()
            var point = CGPointZero
            var index = 0
            CGPointMakeWithDictionaryRepresentation((array[index++] as! CFDictionary), &point)
            
            // 2.1将起点移动到某一个点
            path.moveToPoint(point)
            
            // 2.2连接其它线段
            while index < array.count
            {
                CGPointMakeWithDictionaryRepresentation((array[index++] as! CFDictionary), &point)
                path.addLineToPoint(point)
            }
            // 2.3关闭路径
            path.closePath()
            
            layer.path = path.CGPath
            // 3.将用于保存矩形的图层添加到界面上
            containerLayer.addSublayer(layer)
        }
        
        /// 清空描边
        private func clearLayers()
        {
            guard let subLayers = containerLayer.sublayers else
            {
                return
            }
            for layer in subLayers
            {
                layer.removeFromSuperlayer()
            }
        }
    }
    
    extension QRCodeViewController: UITabBarDelegate
    {
        func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
          //约束改变导致动画有bug所以先移除动画,重新开始就行了
            // 根据当前选中的按钮重新设置二维码容器高度
            containerHeightCons.constant = (item.tag == 1) ? 150 : 300
            view.layoutIfNeeded()
            
            // 移除动画
            scanLineView.layer.removeAllAnimations()
            
            // 重新开启动画
            startAnimation()
        }
    }
    
    

    其他知识点

     1.从数组中取出字典传为CGPoint
     var point = CGPointZero
     CGPointMakeWithDictionaryRepresentation((array[index++] as! CFDictionary), &point)
     2.约束改变导致动画有bug所以先移除动画,重新开始就行了
     3. 创建图层, 用于保存绘制的矩形
        let layer = CAShapeLayer()
     4.    /// 预览图层
        private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)  
    

    相关文章

      网友评论

          本文标题:摄像头抓捕二维码

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