二维码原生扫描
参考博客:
http://www.cnblogs.com/lzjsky/p/5057134.html
http://my.oschina.net/u/2340880/blog/405847
http://www.jianshu.com/p/d24282dd6e72
http://c0ming.me/qr-code-scan/
遇到的bug:
[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found. Use -availableMetadataObjectTypes.'
原因:输出格式要在加入session后设置
(http://stackoverflow.com/questions/25480636/avcapturemetadataoutput-setmetadataobjecttypes-unspported-type-found)
Demo
//
// ErweimaView.m
// 二维码扫描Demo
//
// Created by 小木 on 16/5/17.
// Copyright © 2016年 小木. All rights reserved.
//
#import "ErweimaView.h"
@interface ErweimaView()<AVCaptureMetadataOutputObjectsDelegate>
/**
* AVCaptureSession 管理输入(AVCaptureInput)和输出(AVCaptureOutput)流,包含开启和停止会话方法。
*/
@property (nonatomic, strong) AVCaptureSession *session;
/**
* AVCaptureDeviceInput 是AVCaptureInput的子类,可以作为输入捕获会话,用AVCaptureDevice实例初始化。
*/
@property (nonatomic, strong) AVCaptureDeviceInput *input;
/**
* AVCaptureDevice 代表了物理捕获设备如:摄像机。用于配置等底层硬件设置相机的自动对焦模式。
*/
@property (nonatomic, strong) AVCaptureDevice *device;
/**
* AVCaptureMetadataOutput 是AVCaptureOutput的子类,处理输出捕获会话。捕获的对象传递给一个委托实现AVCaptureMetadataOutputObjectsDelegate协议。协议方法在指定的派发队列(dispatch queue)上执行。
*/
@property (nonatomic, strong) AVCaptureMetadataOutput *output;
/**
* AVCaptureVideoPreviewLayerCALayer的一个子类,显示捕获到的相机输出流。
*/
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *outputLayer;
/**
* 绿线指示器
*/
@property (nonatomic, strong) UIView *indirectorView;
@end
@implementation ErweimaView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self.layer addSublayer:self.outputLayer];
UIView *bgView = [[UIView alloc]initWithFrame:frame];
bgView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
//创建遮罩
UIBezierPath *path = [UIBezierPath bezierPathWithRect:frame];
[path appendPath:[[UIBezierPath bezierPathWithRect:CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200)] bezierPathByReversingPath]];
[self addSubview:bgView];
CAShapeLayer *bgLayer = [CAShapeLayer layer];
bgLayer.path = path.CGPath;
[bgView.layer setMask:bgLayer];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(inputPortChange) name:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil];
}
return self;
}
#pragma mark==========Function
- (void)inputPortChange{
self.output.rectOfInterest = [self.outputLayer metadataOutputRectOfInterestForRect:CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200)];
}
- (BOOL)startScan{
[self.session startRunning];
[self beginScanAnimation];
return YES;
}
- (void)beginScanAnimation{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(scanAnimation) userInfo:nil repeats:YES];
[timer fire];
}
- (void)scanAnimation{
self.indirectorView.frame = CGRectMake(10, 0, 180, 1);
[UIView animateWithDuration:3 animations:^{
self.indirectorView.frame = CGRectMake(10, 200, 180, 1);
}];
}
#pragma mark==========扫描二维码
//创建回话
- (AVCaptureSession *)session{
if (!_session) {
_session = [[AVCaptureSession alloc]init];
//设置高质量采集率
[_session setSessionPreset:AVCaptureSessionPresetHigh];
//添加输入流
[_session addInput:self.input];
//添加输出流
[_session addOutput:self.output];
//设置扫码支持的编码格式(如下设置条形码和二维码兼容)
[self.output setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
}
return _session;
}
//创建输入流
- (AVCaptureDeviceInput *)input{
if (!_input) {
_input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
}
return _input;
}
//创建输入流
- (AVCaptureMetadataOutput *)output{
if (!_output) {
_output = [[AVCaptureMetadataOutput alloc]init];
//设置代理 在主线程里刷新
[_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
}
return _output;
}
//创建输出对象图层
- (AVCaptureVideoPreviewLayer *)outputLayer{
if (!_outputLayer) {
_outputLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
[_outputLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
_outputLayer.frame = self.bounds;
_outputLayer.backgroundColor = [UIColor blackColor].CGColor;
AVCaptureVideoPreviewLayer *boardLayer = [[AVCaptureVideoPreviewLayer alloc]init];
boardLayer.borderColor = [UIColor greenColor].CGColor;
boardLayer.backgroundColor = [UIColor clearColor].CGColor;
boardLayer.borderWidth = 1;
boardLayer.frame = CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200);
[_outputLayer addSublayer:boardLayer];
[boardLayer addSublayer:self.indirectorView.layer];
}
return _outputLayer;
}
//创建设备
- (AVCaptureDevice *)device{
if (!_device) {
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return _device;
}
- (UIView *)indirectorView{
if (!_indirectorView) {
_indirectorView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 180, 1)];
_indirectorView.backgroundColor = [UIColor greenColor];
}
return _indirectorView;
}
#pragma mark==========扫描二维码结果回掉
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if (metadataObjects.count>0) {
[self.session stopRunning];
AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
//输出扫描字符串
NSLog(@"%@",metadataObject.stringValue);
if (metadataObject.stringValue.length>0) {
[self.session stopRunning];
if ([self.delegate respondsToSelector:@selector(erweimaDidScanWithResult:)]) {
[self.delegate erweimaDidScanWithResult:metadataObject.stringValue];
}
}
}
}
效果图
重点一:扫描范围的设置
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(inputPortChange) name:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil];
}
return self;
}
#pragma mark==========Function
- (void)inputPortChange{
self.output.rectOfInterest = [self.outputLayer metadataOutputRectOfInterestForRect:CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200)];
}
重点二:遮罩层的设置
1,遮罩层就是把扫描区域挖空的view
UIView *bgView = [[UIView alloc]initWithFrame:frame];
bgView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:frame];
[path appendPath:[[UIBezierPath bezierPathWithRect:CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200)] bezierPathByReversingPath]];
[self addSubview:bgView];
CAShapeLayer *bgLayer = [CAShapeLayer layer];
bgLayer.path = path.CGPath;
[bgView.layer setMask:bgLayer];
重点三:扫描区域UI的绘制
这个其实只是装饰效果,不具备任何功能
//扫描区域绘制
AVCaptureVideoPreviewLayer *boardLayer = [[AVCaptureVideoPreviewLayer alloc]init];
boardLayer.borderColor = [UIColor greenColor].CGColor;
boardLayer.backgroundColor = [UIColor clearColor].CGColor;
boardLayer.borderWidth = 1;
boardLayer.frame = CGRectMake(SCREEN_WIDTH*0.5-100, 200, 200, 200);
[_outputLayer addSublayer:boardLayer];
[boardLayer addSublayer:self.indirectorView.layer];
}
return _outputLayer;
}
- (UIView *)indirectorView{
if (!_indirectorView) {
_indirectorView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 180, 1)];
_indirectorView.backgroundColor = [UIColor greenColor];
}
return _indirectorView;
}
原理稀释
扫码是一个从摄像头(input)到 解析出字符串(output) 的过程,用AVCaptureSession 来协调。其中是通过 AVCaptureConnection 来连接各个 input 和 output,还可以用它来控制 input 和 output 的 数据流向

2301

被折叠的 条评论
为什么被折叠?



