zoukankan      html  css  js  c++  java
  • Swift开发小技巧--扫描二维码,二维码的描边与锁定,设置扫描范围,二维码的生成(高清,无码,你懂得!)

    二维码的扫描,二维码的锁定与描边,二维码的扫描范围,二维码的生成(高清,无码,你懂得!),识别相册中的二维码

    扫描二维码用到的三个重要对象的关系,如图:

    1.懒加载各种类
        // 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()
    
            // 设置扫描的范围
            // 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;
            // 3.设置输出对象解析数据时感兴趣的范围
             out.rectOfInterest = CGRect(x: x, y: y,  width, height: height)
    
            return out
        }()
    
        /// 预览图层 -- 显示相机扫描到的影像
        private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
    
        /// 专门用于保存描边的图层 -- 方便删除没用的描边
        private lazy var containerLayer: CALayer = CALayer()
    
    2.开始扫描二维码
        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.设置监听(这里是self当前控制器监听),监听输出对象解析到的数据,遵守协议`AVCaptureMetadataOutputObjectsDelegate`
            output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
    
            // 6.添加预览图层,用来展现影像
            view.layer.insertSublayer(previewLayer, atIndex: 0)
            previewLayer.frame = view.bounds
    
            // 7.添加容器图层,containerLayer -- 专门用来存储描边的图层
            view.layer.addSublayer(containerLayer)
            containerLayer.frame = view.bounds
    
            // 8.开始扫描
            session.startRunning()
    
        }
    
    3.AVCaptureMetadataOutputObjectsDelegate代理方法,监听到扫描到的数据就会调用的方法 -- func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!)
    • 参数metadataObjects中有扫描到的数据metadataObjects.last?.stringValue
    4.做完上述三步,就可以读取到二维码中的数据了.这一步做的是二维码的描边与锁定(作用: 当用户扫描范围内有N多二维码的时候,需要告诉用户当前扫中的是哪个)
    • 需要用到的参数也在metadataObjects中,代码如下:
        /// 只要扫描到结果就会调用
        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()
            }
        }
    
    5.扫描范围,只有在指定区域内才能扫描出数据 -- 输出对象中可以设置扫描的范围,通过属性rectOfInterest来设置,属性中的值为比例值,而且是以横屏时左上角为坐标原点
        /// 输出对象 -- 可以设置扫描范围
        private lazy var output: AVCaptureMetadataOutput = {
           let out = AVCaptureMetadataOutput()
    
            // 设置扫描的范围
            // 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;
            // 3.设置输出对象解析数据时感兴趣的范围
             out.rectOfInterest = CGRect(x: x, y: y,  width, height: height)
    
            return out
        }()
    
    6.二维码的生成,普通情况下生成的二维码图片不够清晰,代码中提供了一个返回高清图片的方法
        /// 二维码容器
        @IBOutlet weak var customImageVivew: UIImageView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // 1.创建滤镜
            let filter = CIFilter(name: "CIQRCodeGenerator")
            // 2.还原滤镜默认属性
            filter?.setDefaults()
            // 3.设置需要生成二维码的数据到滤镜中
            // OC中要求设置的是一个二进制数据
            filter?.setValue("关注Chaos_G的博客".dataUsingEncoding(NSUTF8StringEncoding), forKeyPath: "InputMessage")
            // 4.从滤镜从取出生成好的二维码图片
            guard let ciImage = filter?.outputImage else
            {
                return
            }
        
    //        customImageVivew.image = UIImage(CIImage: ciImage)
            customImageVivew.image = createNonInterpolatedUIImageFormCIImage(ciImage, size: 500)
            
        }
        
        /**
         生成高清二维码
         
         - parameter image: 需要生成原始图片
         - parameter size:  生成的二维码的宽高
         */
        private func createNonInterpolatedUIImageFormCIImage(image: CIImage, size: CGFloat) -> UIImage {
            
            let extent: CGRect = CGRectIntegral(image.extent)
            let scale: CGFloat = min(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent))
            
            // 1.创建bitmap;
            let width = CGRectGetWidth(extent) * scale
            let height = CGRectGetHeight(extent) * scale
            let cs: CGColorSpaceRef = CGColorSpaceCreateDeviceGray()!
            let bitmapRef = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, cs, 0)!
            
            let context = CIContext(options: nil)
            let bitmapImage: CGImageRef = context.createCGImage(image, fromRect: extent)
            
            CGContextSetInterpolationQuality(bitmapRef,  CGInterpolationQuality.None)
            CGContextScaleCTM(bitmapRef, scale, scale);
            CGContextDrawImage(bitmapRef, extent, bitmapImage);
            
            // 2.保存bitmap到图片
            let scaledImage: CGImageRef = CGBitmapContextCreateImage(bitmapRef)!
            
            return UIImage(CGImage: scaledImage)
        }
    
  • 相关阅读:
    JNDI 是什么
    RuntimeException和非RuntimeException的区别
    dynamicinsert,dynamicupdate能够性能上的少许提升
    Session,有没有必要使用它?[转]
    c# textbox中光标所在行命令及选中命令移动到最后一行且光标提前[转]
    C#分布式事务(TransactionScope )
    .net中的分布式事务
    大道至简,职场上做人做事做管理[转]
    C#中TreeView的CheckBox的两种级联选择
    C# winform TreeView中关于checkbox选择的完美类[转]
  • 原文地址:https://www.cnblogs.com/gchlcc/p/5638940.html
Copyright © 2011-2022 走看看