二维码的生成 识别 扫描 封装(Swift)
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()
}
}
}
}