Hello Swift iOS学习首页投稿(暂停使用,暂停投稿)

Swift中二维码的相关操作

2016-11-18  本文已影响527人  iiNico

二维码

生成二维码

生成二维码实例

/// 生成二维码
extension QRCodeTool {
    /// 生成二维码
    ///
    /// - Parameters:
    ///   - contentStr: 二维码内容
    ///   - bigImageWH: 二维码大小
    ///   - smallImage: 小图标
    ///   - smallImageSize: 小图标大小
    /// - Returns: 生成一个自顶一个二维码图片返回
    class func gerneratorQRCode(contentStr : String, bigImageWH :  CGFloat, smallImage : UIImage, smallImageSize : CGFloat) -> UIImage? {
        // 1.创建一个二维码滤镜
        guard let filter = CIFilter(name: "CIQRCodeGenerator") else {return nil}
        
        // 1.1恢复默认值
        filter.setDefaults()
        
        // 2.设置内容
        // 注意点
        // 如果要设置二维码内容,必须以KVC的方式设置
        // 二维码的值必须是NSData
        let data = contentStr.data(using: .utf8)
        filter.setValue(data, forKeyPath: "inputMessage")
        
        // 2.1设置二维码的级别(纠错率)
        //        key : inputCorrectionLevel
        //        value L: 7% M(默认  ): 15% Q: 25% H: 30%
        filter.setValue("H", forKeyPath: "inputCorrectionLevel")
        
        // 3.从二维码滤镜中获取二维码
        guard let outputImage = filter.outputImage else {return nil}
        
        // 4.展示图片(生成的二维码图片的大小为23*23,放大到200*200就会变得很模糊)
        //        let imageUI = UIImage(ciImage: outputImage)
        //        print(imageUI.size)
        guard let imageUI = createClearImage(outputImage, size: bigImageWH) else {return nil}
        
        // 4.设置二维码颜色(注意:设置二维码颜色,必须放在清晰的二维码之后,如果先设置颜色,再改变清晰度,则设置颜色无效)
        let imageCI = CIImage(image: imageUI)
        let colorFiler = CIFilter(name: "CIFalseColor")
        colorFiler?.setDefaults()
        // 设置图片
        colorFiler?.setValue(imageCI, forKeyPath: "inputImage")
        // 设置二维码亚瑟
        colorFiler?.setValue(CIColor.init(color: UIColor.yellow), forKeyPath: "inputColor0")
        // 设置背景颜色
        colorFiler?.setValue(CIColor.init(color: UIColor.red), forKeyPath: "inputColor1")
        guard let colorOutputImage = colorFiler?.outputImage else {return nil}
        let image  = UIImage(ciImage: colorOutputImage)
        return createCustomImage(bigImage: image, smallImage: smallImage, smallImageWH: smallImageSize)
    }

    /// 自定义二维码
    ///
    /// - Parameters:
    ///   - bigImage: 二维码图片
    ///   - smallImage: 小图片
    ///   - smallImageWH: 小图片尺寸
    /// - Returns: 生成新的二维码图片
    fileprivate class func createCustomImage(bigImage : UIImage, smallImage : UIImage, smallImageWH : CGFloat) -> UIImage? {
        
        // 0.大图片的尺寸
        let bigImageSize = bigImage.size
        
        // 1.创建图形上下文
        UIGraphicsBeginImageContext(bigImageSize)
        
        // 2.绘制大图片
        bigImage.draw(in: CGRect(x: 0, y: 0, width: bigImageSize.width, height: bigImageSize.height))
        
        // 3.绘制小图片
        smallImage.draw(in: CGRect(x: (bigImageSize.width - smallImageWH) * 0.5, y: (bigImageSize.height - smallImageWH) * 0.5, width: smallImageWH, height: smallImageWH))
        
        // 4.从图形上下文中取出图片
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        // 5.关闭图形上下文
        UIGraphicsEndImageContext()
        
        // 6.返回图片
        return image
        
    }
    
    /// 将模糊的二维码图片转为清晰的
    ///
    /// - parameter image: 模糊的二维码图片
    /// - parameter size: 需要生成的二维码尺寸
    ///
    /// - returns: 清晰的二维码图片
    fileprivate class func createClearImage(_ image : CIImage, size : CGFloat ) -> UIImage? {
        
        // 1.调整小数像素到整数像素,将origin下调(12.*->12),size上调(11.*->12)
        let extent = image.extent.integral
        
        // 2.将指定的大小与宽度和高度进行对比,获取最小的比值
        let scale = min(size / extent.width, size/extent.height)
        
        // 3.将图片放大到指定比例
        let width = extent.width * scale
        let height = extent.height * scale
        
        // 3.1创建依赖于设备的灰度颜色通道
        let cs = CGColorSpaceCreateDeviceGray();
        
        // 3.2创建位图上下文
        let bitmapRef = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: 0)
        
        // 4.创建上下文
        let context = CIContext(options: nil)
        
        // 5.将CIImage转为CGImage
        let bitmapImage = context.createCGImage(image, from: extent)
        
        // 6.设置上下文渲染等级
        bitmapRef!.interpolationQuality = .none
        
        // 7.改变上下文的缩放
        bitmapRef?.scaleBy(x: scale, y: scale)
        
        // 8.绘制一张图片在位图上下文中
        bitmapRef?.draw(bitmapImage!, in: extent)
        
        // 9.从位图上下文中取出图片(CGImage)
        guard let scaledImage = bitmapRef?.makeImage() else {return nil}
        
        // 10.将CGImage转为UIImage并返回
        return UIImage(cgImage: scaledImage)
    }
}

效果

识别二维码

识别二维码实例

extension QRCodeTool {
    /// 识别图片中的二维码
    ///
    /// - Parameter sourceImage: 原始图片
    /// - Returns: 将识别到的二维码绘制边框并返回图片
    class func detectorQRCode(sourceImage : UIImage) -> UIImage? {
        // 0.创建上下文
        let context = CIContext()
        
        // 1.创建二维码的探测器
        guard let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: context, options: [CIDetectorAccuracy : CIDetectorAccuracyHigh]) else {return nil}
        
        // 2.探测图片
        guard let sourceImageCI = CIImage(image: sourceImage) else {return nil}
        guard let features = detector.features(in: sourceImageCI) as? [CIQRCodeFeature] else {return nil}
        
        // 3.绘制识别的二维码的边框
        return drawQRCodeBorder(features: features, sourceImage: sourceImage)
    }
    
    fileprivate class func drawQRCodeBorder(features : [CIQRCodeFeature], sourceImage : UIImage) -> UIImage? {
        
        // 0.源图片大小
        let sourceImageSize = sourceImage.size
        
        // 1.创建图形上下文
        UIGraphicsBeginImageContext(sourceImageSize)
        
        // 2.绘制图片
        sourceImage.draw(in: CGRect(x: 0, y: 0, width: sourceImageSize.width, height: sourceImageSize.height))
        
        // 3.绘制边框(bounds的坐标原点在左下角)
        // 第二种解决方式
        // 获取图形上下文
        let context = UIGraphicsGetCurrentContext()
        
        // 改变图形上下文的坐标原点
        // 注意:必须放在绘制之前
        context?.scaleBy(x: 1, y: -1)
        context?.translateBy(x: 0, y: -sourceImageSize.height)
        for feature in features {
            let bounds = feature.bounds
            // 第一种解决方式
            // let newBounds = CGRect(x: bounds.origin.x, y:sourceImageSize.height - bounds.origin.y - bounds.height , width: bounds.width, height: bounds.height)
            
            // let path = UIBezierPath(rect: newBounds)
            let path = UIBezierPath(rect: bounds)
            UIColor.red.set()
            path.lineWidth = 4
            path.stroke()
        }
        
        // 4.取出图片
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        // 5.关闭图形上下文
        UIGraphicsEndImageContext()
        
        // 6.返回图片
        return image
    }
}

效果

扫描二维码

class QRCodeTool: NSObject {
    /// 扫描二维码
    
    // 单例对象
    static let shareInstance = QRCodeTool()
    
    ///懒加载
    /// 创建会话(顺便添加输入和输出对象)
    fileprivate lazy var session : AVCaptureSession? = {
        // 1.获取摄像头设备
        guard let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) else {return nil}
        
        // 2.将摄像头作为输入对象
        var input : AVCaptureDeviceInput?
        do {
            input = try AVCaptureDeviceInput(device: captureDevice)
            
        } catch {
            print(error)
            return nil
        }
        
        // 3.创建输出对象
        let output = AVCaptureMetadataOutput()
        output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        
        // 4.创建捕捉会话
        let session = AVCaptureSession()
        
        // 5.添加输入对象和输出对象到会话中
        if session.canAddInput(input!) && session.canAddOutput(output)  {
            session.addInput(input!)
            session.addOutput(output)
            
            // 注意:如果要扫描任何码制,必须制定扫描的类型
            // 必须在添加了输出对象之后再设置
            output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
        }
        return session
    }()
    
    /// 创建预览图层
    /// 将预览图层添加到layer上,注意添加到最底层,否则会遮住当前的控件
    fileprivate lazy var preViewLayer : AVCaptureVideoPreviewLayer? = {
        guard let preViewLayer = AVCaptureVideoPreviewLayer(session: self.session) else {return nil}
        return preViewLayer
    }()
    
    // 定义闭包
    typealias ScanResultBlock = (_ resultStr : String) -> ()
    
    var scanResultBlock : ScanResultBlock?
    
    func scanQRCode(scanView : UIView, preView : UIView, scanResultBlock : @escaping ScanResultBlock) -> () {
        
        // 0.记录block
        self.scanResultBlock = scanResultBlock
        
        // 1.判断会话和预览图层是否有值
        if session == nil || preViewLayer == nil {
            return
        }
        
        if let subLayers = preView.layer.sublayers {
            let isHavePreViewLayer = subLayers.contains(preViewLayer!)
            if !isHavePreViewLayer {
                preView.layer.insertSublayer(preViewLayer!, at: 0)
                preViewLayer?.frame = preView.bounds
                
                guard let output = session!.outputs.first as? AVCaptureMetadataOutput  else {
                    return
                }
                // 设置扫描区域(内部会以坐标原点在右上角设置)
                let x = scanView.frame.origin.y / preView.bounds.height
                let y = scanView.frame.origin.x / preView.bounds.width
                let w = scanView.bounds.height / preView.bounds.height
                let h = scanView.bounds.width / preView.bounds.width
                output.rectOfInterest = CGRect(x: x, y: y, width: w, height: h)
            }
        }
        // 2.判断是否正在扫描
        if session!.isRunning {
            return
        }
        // 3.开始扫描
        session!.startRunning()
    }
    
    
    /// 控制手机手电筒
    /// 步骤 
    /// 1.需要使用AVFoundation框架
    /// 2.获取手机摄像头
    /// 3.通过摄像头获取硬件的控制权
    /// 4.设置手电筒的模式
    /// 5.取消硬件的控制权
    class func turnLight(isOn : Bool) {
        
        // 1.获取摄像头
        guard let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) else {return}
        
        // 2.获取设备的控制权
        do {
            try captureDevice.lockForConfiguration()
        } catch {
            print(error)
            return
        }
        
        if isOn { // 打开手电筒
            captureDevice.torchMode = .on
            
        } else { // 关闭手电筒
            captureDevice.torchMode = .off
        }
        
        // 3.释放设备的控制权
        captureDevice.unlockForConfiguration()
    }
}

/// AVCaptureMetadataOutputObjectsDelegate
extension QRCodeTool : AVCaptureMetadataOutputObjectsDelegate {
    
    /// 当扫描到结果的时候就会来到该方法(任何码制都会来到该方法)
    ///
    /// - Parameters:
    ///   - captureOutput: 输出
    ///   - metadataObjects: 扫描到码制数组
    ///   - connection: 链接
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
        
        removeQRCodeBorder()
        
        // 扫描的类型AVMetadataMachineReadableCodeObject(二维码类型)
        // 这里只处理二维码
        // 当移除屏幕的时候,系统会额外调用一次该方法,内容是空
        
        guard let result = metadataObjects.first as? AVMetadataMachineReadableCodeObject else {return}
        scanResultBlock!(result.stringValue)
        drawQRCodeBorder(result: result)
        
//        guard let results = metadataObjects as? [AVMetadataMachineReadableCodeObject] else {return}
//        for result in results {
//            //            print(result.stringValue, result.corners)
//            drawQRCodeBorder(result: result)
//        }
        
    }
    
    /// 给二维码绘制边框
    ///
    /// - Parameter result: 扫描到的二维码结果
    private func drawQRCodeBorder(result : AVMetadataMachineReadableCodeObject) {
        
        // result.corners:是数据坐标,必须使用预览图层将坐标转为位(上下文)的坐标
        guard let resultObj = preViewLayer?.transformedMetadataObject(for: result) as? AVMetadataMachineReadableCodeObject else {return}
        // 绘制边框
        let path = UIBezierPath()
        var index = 0
        for corner in resultObj.corners {
            
            let dictCF = corner as! CFDictionary
            let point = CGPoint(dictionaryRepresentation: dictCF)!
            
            // 开始绘制
            if index == 0 {
                path.move(to: point)
            } else {
                path.addLine(to: point)
            }
            
            index = index + 1
        }
        
        // 关闭路径
        path.close()
        
        // 创建形状图层
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.lineWidth = 3
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        preViewLayer?.addSublayer(shapeLayer)
    }
    
    /// 移除边框
    private func removeQRCodeBorder() {
        
        if let layers = preViewLayer?.sublayers {
            for layer in layers {
                let shapeLayer = layer as? CAShapeLayer
                if shapeLayer != nil {
                    shapeLayer?.removeFromSuperlayer()
                }
            }
        }   
    }
}

效果

上一篇下一篇

猜你喜欢

热点阅读