二维码的生成 识别 扫描 封装(Swift)

二维码的生成 识别 扫描 封装(Swift)

直接上代码 封装成一个工具类 直接在VC中 QRCodeTool.detectorQRCodeImage调用即可

import Foundation
import UIKit
import AVFoundation

typealias ScanResult = ([String]?)->()

class QRCodeTool: NSObject {

static let shareInstance = QRCodeTool()

lazy var input: AVCaptureDeviceInput? = {

    // 1. 获取摄像头设备, 并作为输入设备
    let device: AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    var input: AVCaptureDeviceInput?
    do
    {
        input = try AVCaptureDeviceInput(device: device)
    }catch
    {
        return nil
    }

    return input

}()

lazy var output: AVCaptureMetadataOutput = {
    // 2. 设置元数据输出处理
    let output: AVCaptureMetadataOutput = AVCaptureMetadataOutput()
    output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
    return output
}()

lazy var session: AVCaptureSession = {
    let session = AVCaptureSession()
    return session

}()

lazy var preLayer: AVCaptureVideoPreviewLayer = {

    return AVCaptureVideoPreviewLayer(session: self.session)

}()

var scanResult: ScanResult?

var isDraw: Bool = false


}

/// 二维码扫描接口
extension QRCodeTool {

// 接口3: 开始扫描二维码(是否描绘二维码边框)
func startScan(inView: UIView, isDrawQRCodeFrame: Bool, result: ScanResult)
{

    scanResult = result
    isDraw = isDrawQRCodeFrame

    // 3. 创建会话, 连接输入和输出
    if session.canAddInput(input) && session.canAddOutput(output)
    {
        session.addInput(input)
        session.addOutput(output)
    }
    // 3.1 设置元数据处理类型 output.availableMetadataObjectTypes
    // 必须在添加元数据输出之后, 才可以设置, 否则无效
    output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]

    // 3.2 添加预览图层
    preLayer.frame = inView.layer.bounds
    let subLayers: NSArray = inView.layer.sublayers! as NSArray
    if !subLayers.containsObject(preLayer)
    {
        inView.layer.insertSublayer(preLayer, atIndex: 0)
    }

    // 4. 启动会话
    session.startRunning()



}

// 接口4: 停止扫描二维码
func stopScan()
{
    session.stopRunning()
}

// 接口5: 设置兴趣区域
func setInterestRect(sourceRect: CGRect)
{
    // 3.3 设置兴趣区域
    // 注意, 此处需要填的rect, 是以右上角为(0, 0), 也就是横屏状态
    // 值域范围: 0->1
    let bounds = UIScreen.mainScreen().bounds
    let x = sourceRect.origin.x / bounds.size.width
    let y = sourceRect.origin.y / bounds.size.height
    let w = sourceRect.size.width / bounds.size.width
    let h = sourceRect.size.height / bounds.size.height

    output.rectOfInterest = CGRectMake(y, x, h, w)

}

}

/// 二维码生成和识别
extension QRCodeTool {
// 接口1: 根据一个字符串, 生成一个二维码, 并且返回
class func generatorQRCodeImage(contentStr: String?, centerImage: UIImage?) -> UIImage? {

    // 0. 获取二维码存储信息
    guard let str = contentStr else { return nil}

    // 1. 创建一个滤镜
    let fileter: CIFilter?  = CIFilter(name: "CIQRCodeGenerator")
    // 1.1 恢复滤镜默认设置
    fileter?.setDefaults()
    // 2. 设置二维码滤镜的输入数据, 需要通过KVC, 设置 inputMessage字段, 值为NSData
    fileter?.setValue(str.dataUsingEncoding(NSUTF8StringEncoding), forKey: "inputMessage")
    // 2.1 设置二维码纠错率, 需要通过KVC, 设置 inputCorrectionLevel字段, 值为L, M, Q, H
    fileter?.setValue("M", forKey: "inputCorrectionLevel")
    // 3. 获取输出图片(默认大小是23 X 23)
    let image = fileter?.outputImage
    //        let imageUI = UIImage(CIImage: image!)
    //        print(imageUI.size)

    // 3.1 后期处理, 可以借助位图的方式, 放大图片(没有必要掌握)
    let imageUI = createBigImage(image!, size: 200)

    if centerImage == nil
    {
        return imageUI
    }else
    {
        return addCeneterImage(centerImage!, sizeHW: 44, toImage: imageUI)
    }


}

// 接口2: 根据给定的一张图片, 识别出图片中所有二维码, 返回结果数组, 并且描绘识别的二维码边框
class func detectorQRCodeImage(qrCodeImage: UIImage, isDrawRect: Bool, result: ([String], UIImage?)->())
{

    // 1. 创建一个二维码探测器
    let context = CIContext()
    let detector: CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: context, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])


    // 2. 识别图片特征
    let imageCI = CIImage(image: qrCodeImage)
    let features: [CIFeature] = detector.featuresInImage(imageCI!)

    var resultArray = [String]()

    var tempImage = qrCodeImage
    for feature in features
    {
        if feature.isKindOfClass(CIQRCodeFeature){
            let qrFeature = feature as! CIQRCodeFeature
            resultArray.append(qrFeature.messageString)

            if isDrawRect
            {
                tempImage = drawRectInImage(qrFeature, image: tempImage)
            }

        }

        if feature.isKindOfClass(CITextFeature) {

            let textFeatrue = feature as! CITextFeature
            print(textFeatrue.subFeatures)

        }

    }

    // 通过block 返回结果
    result(resultArray, tempImage)


}



/// 私有方法
 extension QRCodeTool
{

private class func drawRectInImage(qrFeature: CIQRCodeFeature, image: UIImage) -> UIImage
{

    // 1. 开启图形上下文
    UIGraphicsBeginImageContext(image.size)

    // 2. 绘制图片
    image.drawInRect(CGRectMake(0, 0, image.size.width, image.size.height))

    // 3. 上下颠倒坐标系
    let context = UIGraphicsGetCurrentContext()
    CGContextScaleCTM(context, 1.0, -1.0)
    CGContextTranslateCTM(context, 0, -image.size.height)

    // 4. 绘制识别的二维码的矩形框
    let path = UIBezierPath(rect: qrFeature.bounds)
    UIColor.redColor().set()
    path.lineWidth = 6
    path.stroke()

    // 5. 取出图片
    let image = UIGraphicsGetImageFromCurrentImageContext()

    // 6. 关闭图形上下文
    UIGraphicsEndImageContext()

    return image

}





private class func addCeneterImage(centerImage: UIImage, sizeHW: CGFloat, toImage: UIImage) -> UIImage {

    // 1. 开启一个图形上下文
    let bigSize = toImage.size
    UIGraphicsBeginImageContext(bigSize)

    // 2. 绘制大图片
    toImage.drawInRect(CGRectMake(0, 0, bigSize.width, bigSize.height))

    // 3. 在中间绘制小图片
    let x = (bigSize.width - sizeHW) / 2
    let y = (bigSize.height - sizeHW) / 2
    let w = sizeHW
    let h = sizeHW
    centerImage.drawInRect(CGRectMake(x, y, w, h))

    // 4. 获取最终合成图片
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()

    // 5. 关闭上下文
    UIGraphicsEndImageContext()

    // 6. 返回图片
    return resultImage
}


/**
 根据CIImage生成指定大小的高清UIImage
 :param: image 指定CIImage
 :param: size    指定大小
 :returns: 生成好的图片
 */
private class func createBigImage(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)
}




}

extension QRCodeTool: AVCaptureMetadataOutputObjectsDelegate
{
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {

    removeQRCodeFrame()

    var tempArray = [String]()

    for obj in metadataObjects
    {
        if obj.isKindOfClass(AVMetadataMachineReadableCodeObject)
        {
            var codeObj = obj as! AVMetadataMachineReadableCodeObject
            //                print(codeObj.stringValue)
            tempArray.append(codeObj.stringValue)
            // corners


            // 使用预览图层转换坐标系
            codeObj = preLayer.transformedMetadataObjectForMetadataObject(codeObj) as! AVMetadataMachineReadableCodeObject
            //                 print(codeObj.corners)

            if isDraw
            {
                drawQRCodeFrame(codeObj)
            }

        }
    }

    guard let resultBlock = scanResult else { return }
    resultBlock(tempArray)


}


func drawQRCodeFrame(codeObj: AVMetadataMachineReadableCodeObject) {

    let layer: CAShapeLayer = CAShapeLayer()
    layer.lineWidth = 6
    layer.strokeColor = UIColor.redColor().CGColor
    layer.fillColor = UIColor.clearColor().CGColor


    let path: UIBezierPath = UIBezierPath()


    let count = codeObj.corners.count

    for i in 0..<count
    {
        let pointDic = codeObj.corners[i] as! CFDictionaryRef
        // 根据字典创建一个point
        var point = CGPointZero
        CGPointMakeWithDictionaryRepresentation(pointDic, &point)

        if i == 0
        {
            path.moveToPoint(point)
        }else
        {
            path.addLineToPoint(point)
        }

    }

    path.closePath()


    layer.path = path.CGPath

    preLayer.addSublayer(layer)



}

func removeQRCodeFrame()
{
    guard let subLayers = preLayer.sublayers else { return }
    for layer in subLayers
    {
        if layer.isKindOfClass(CAShapeLayer)
        {
            layer.removeFromSuperlayer()
        }
    }
}




}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值