摄像头抓捕二维码
AVCaptureDeviceInput,AVCaptureSession,AVCaptureMetadataOutput,AVCaptureVideoPreviewLayer
AVMetadataMachineReadableCodeObject,AVMetadataObject
相关类
/// 输入对象
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()
// 设置输出对象解析数据时感兴趣的范围
// 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)全屏比例算
// 通过对这个值的观察, 我们发现传入的是比例
// 注意: 参照是以横屏(竖屏逆时针90度,Landscape Left)的左上角作为, 而不是以竖屏
// out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
// 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;
out.rectOfInterest = CGRect(x: x, y: y, width: width, height: height)
return out
}()
/// 预览图层
private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
/// 专门用于保存描边的图层
private lazy var containerLayer: CALayer = CALayer()
}
// 1.判断输入能否添加到会话中
// 2.判断输出能够添加到会话中
// 3.添加输入和输出到会话中
// 4.设置输出能够解析的数据类型
// 注意点: 设置数据类型一定要在输出对象添加到会话之后才能设置
// 5.设置监听监听输出解析到的数据
// 6.添加预览图层
// 7.添加容器图层,专门用于保存描边的图层
// 8.开始扫描
// QRCodeViewController.swift
import UIKit
import AVFoundation
class QRCodeViewController: UIViewController {
/// 扫描容器
@IBOutlet weak var customContainerView: UIView!
/// 底部工具条
@IBOutlet weak var customTabbar: UITabBar!
/// 结果文本
@IBOutlet weak var customLabel: UILabel!
/// 容器视图高度约束
@IBOutlet weak var containerHeightCons: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 1.设置默认选中
customTabbar.selectedItem = customTabbar.items?.first
// 2.添加监听, 监听底部工具条点击
customTabbar.delegate = self
// 3.开始扫描二维码
scanQRCode()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
startAnimation()
}
// MARK: - 内部控制方法
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.设置监听监听输出解析到的数据
output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
// 6.添加预览图层
view.layer.insertSublayer(previewLayer, atIndex: 0)
previewLayer.frame = view.bounds
// 7.添加容器图层,专门用于保存描边的图层
view.layer.addSublayer(containerLayer)
containerLayer.frame = view.bounds
// 8.开始扫描
session.startRunning()
}
}
@IBAction func photoBtnClick(sender: AnyObject) {
}
@IBAction func closeBtnClick(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
// 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()
// 设置输出对象解析数据时感兴趣的范围
// 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)全屏比例算
// 通过对这个值的观察, 我们发现传入的是比例
// 注意: 参照是以横屏(竖屏逆时针90度,Landscape Left)的左上角作为, 而不是以竖屏
// out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
// 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;
out.rectOfInterest = CGRect(x: x, y: y, width: width, height: height)
return out
}()
/// 预览图层
private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
/// 专门用于保存描边的图层
private lazy var containerLayer: CALayer = CALayer()
}
extension QRCodeViewController: AVCaptureMetadataOutputObjectsDelegate
{
/// 只要扫描到结果就会调用
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()
}
}
}
extension QRCodeViewController: UITabBarDelegate
{
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
//约束改变导致动画有bug所以先移除动画,重新开始就行了
// 根据当前选中的按钮重新设置二维码容器高度
containerHeightCons.constant = (item.tag == 1) ? 150 : 300
view.layoutIfNeeded()
// 移除动画
scanLineView.layer.removeAllAnimations()
// 重新开启动画
startAnimation()
}
}
其他知识点
1.从数组中取出字典传为CGPoint
var point = CGPointZero
CGPointMakeWithDictionaryRepresentation((array[index++] as! CFDictionary), &point)
2.约束改变导致动画有bug所以先移除动画,重新开始就行了
3. 创建图层, 用于保存绘制的矩形
let layer = CAShapeLayer()
4. /// 预览图层
private lazy var previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
网友评论