二维码/条形码工作框图:
输入设备 <————> 会话 <————> 输出设备
设置扫描区域
- 特别要注意的是输出设备的属性:rectOfInterest的坐标系是横屏坐标系,因此要进行转换
-
1. 懒加载
//1,输入设备
private lazy var inputDevice:AVCaptureDeviceInput? = {
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
return try! AVCaptureDeviceInput.init(device: device)
}()
//2,会话
private lazy var session:AVCaptureSession? = {
return AVCaptureSession.init()
}()
//3,输出设备,设置输出对象解析数据时聚焦的区域,默认值是全屏幕,也就是屏幕哪里扫到了二维码就聚焦哪, CGRect(x: 0, y: 0, width: 1, height: 1),现在要求只聚焦制定框内的二维码
private lazy var outputDevice:AVCaptureMetadataOutput? = {
let output = 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;
output.rectOfInterest = CGRect(x: x, y: y, width: width, height: height)
return output
}()
//4,预览扫描
private lazy var previewLayer:AVCaptureVideoPreviewLayer = {
return AVCaptureVideoPreviewLayer.init(session: self.session)
}()
//5,聚焦框
private lazy var focusLayer:CALayer = {
var va = CALayer.init()
return va
}()
2.开始扫描
//二维码扫描
func QRScanning() -> Void {
//1,确认session是否可以添加输入设备
if !(session?.canAddInput(inputDevice))!{
return
}
//2,确认session是否可以添加输出设备
if !(session?.canAddOutput(outputDevice))! {
return
}
//3,添加输入输出设备
session?.addInput(inputDevice)
session?.addOutput(outputDevice)
//4,设置输出设备能够解析的数据
outputDevice?.metadataObjectTypes = outputDevice?.availableMetadataObjectTypes
//5,设置监听输出解析到的数据,调用代理方法
outputDevice?.setMetadataObjectsDelegate(self, queue:dispatch_get_main_queue())
//6,添加预览图层,使得预览曾扫描框在一个限定范围内
previewLayer.frame = view.frame
view.layer.insertSublayer(previewLayer, atIndex: 0)
//7,添加自定义的聚焦框
focusLayer.frame = view.bounds
view.layer.addSublayer(focusLayer)
//8,开始扫描
session?.startRunning()
}
3.实现代理方法
extension QRBarCodeViewController:AVCaptureMetadataOutputObjectsDelegate{
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!)
{
//metadataObjects元素类型是AVMetadataObject
guard let metadata: AVMetadataObject? = metadataObjects.last as? AVMetadataObject else{
return
}
//通过预览图层将数据,转换为能识别的数据类型
let canReadData = previewLayer.transformedMetadataObjectForMetadataObject(metadata)
// DyLog(metadata) before:corners:{ 0.5,0.6 0.6,0.6 0.6,0.3 0.5,0.3 },
//DyLog(canReadData as! AVMetadataMachineReadableCodeObject)
//after convert:corners:{ 162.5,309.2 166.7,401.7 260.1,401.6 258.7,307.2 },
//清除之前的图层
clearLayer()
guard let pointArrMetaData = canReadData as? AVMetadataMachineReadableCodeObject else{
return
}
//画框
drawLine(pointArrMetaData)
}
func drawLine(data:AVMetadataMachineReadableCodeObject!) -> Void {
//校验数据,查看数据的数组中是否含有corners,没有直接返回,否则会数组越界
guard let array = data!.corners else{
return
}
//1,创建会话图层,画贝塞尔曲线的图层CAShapeLayer
let drawLayer = CAShapeLayer.init()
//设置边框宽度和颜色
drawLayer.fillColor = UIColor.clearColor().CGColor
drawLayer.strokeColor = UIColor.greenColor().CGColor
drawLayer.lineWidth = 3
//2,创建贝塞尔曲线
let bezierPath = UIBezierPath.init()
var index = 0;
//将数组中的字典转为CGPoint
var start = CGPointZero
CGPointMakeWithDictionaryRepresentation((array[index] as! CFDictionary), &start)
bezierPath.moveToPoint(start)
//3,画框
while index < array.count-1 {
index += 1
DyLog(index)
CGPointMakeWithDictionaryRepresentation((array[index] as! CFDictionary),&start)
bezierPath.addLineToPoint(start)
}
//4,关闭路径
bezierPath.closePath()
//5,将贝塞尔曲线添加到图层上
drawLayer.path = bezierPath.CGPath
//6,将图层添加到聚焦界面上
focusLayer.addSublayer(drawLayer)
}
func clearLayer() -> Void {
guard let sublayers = focusLayer.sublayers else{
return
}
for layer in sublayers {
layer.removeFromSuperlayer()
}
}
}
网友评论