版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么在ios中利用AVFoundation读取二维码
本篇文章给大家分享的是有关怎么在ios中利用AVFoundation读取二维码,在下觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着在下一起来看看吧。1二维码的读取读取二维码也就是通过扫描二维码图像以获取其所包含的数据信息。需要知道的是,任何条形码(包括二维码)的扫描都是基于视频采集(videocapture),因此需要使用AVFoundation框架。扫描二维码的过程即从摄像头捕获二维码图像(input)到解析出字符串内容(output)的过程,主要是通过AVCaptureSession对象来实现的。该对象用于协调从输入到输出的数据流,在执行过程中,需要先将输入和输出添加到AVCaptureSession对象中,然后通过发送startRunning或stopRunning消息来启动或停止数据流,最后通过AVCaptureVideoPreviewLayer对象将捕获的视频显示在屏幕上。在这里,输入对象通常是AVCaptureDeviceInput对象,主要是通过AVCaptureDevice的实例来获得,而输出对象通常是AVCaptureMetaDataOutput对象,它是读取二维码的核心部分,与AVCaptureMetadataOutputObjectsDelegate协议结合使用,可以捕获在输入设备中找到的任何元数据,并将其转换为可读的格式。下面是具体步骤:1、导入AVFoundation框架。#import
<AVFoundation/AVFoundation.h>2、创建一个AVCaptureSession对象。AVCaptureSession
*captureSession
=
[[AVCaptureSession
alloc]
init];3、为AVCaptureSession对象添加输入和输出。//
add
input
NSError
*error;
AVCaptureDevice
*device
=
[AVCaptureDevice
defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput
*deviceInput
=
[AVCaptureDeviceInput
deviceInputWithDevice:device
error:&error];
[captureSession
addInput:deviceInput];
//
add
output
AVCaptureMetadataOutput
*metadataOutput
=
[[AVCaptureMetadataOutput
alloc]
init];
[captureSession
addOutput:metadataOutput];4、配置AVCaptureMetaDataOutput对象,主要是设置代理和要处理的元数据对象类型。dispatch_queue_t
queue
=
dispatch_queue_create("MyQueue",
NULL);
[metadataOutput
setMetadataObjectsDelegate:self
queue:queue];
[metadataOutput
setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];需要注意的是,一定要在输出对象被添加到captureSession之后才能设置要处理的元数据类型,否则会出现下面的错误:Terminatingappduetouncaughtexception'NSInvalidArgumentException',reason:[AVCaptureMetadataOutputsetMetadataObjectTypes:]Unsupportedtypefound-use-availableMetadataObjectTypes'5、创建并设置AVCaptureVideoPreviewLayer对象来显示捕获到的视频。AVCaptureVideoPreviewLayer
*previewLayer
=
[[AVCaptureVideoPreviewLayer
alloc]
initWithSession:captureSession];
[previewLayer
setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[previewLayer
setFrame:self.view.bounds];
[self.view.layer
addSublayer:previewLayer];6、给AVCaptureSession对象发送startRunning消息以启动视频捕获。[captureSession
startRunning];7、实现AVCaptureMetadataOutputObjectsDelegate的captureOutput:didOutputMetadataObjects:fromConnection:方法来处理捕获到的元数据,并将其读取出来。-
(void)captureOutput:(AVCaptureOutput
*)output
didOutputMetadataObjects:(NSArray<__kindof
AVMetadataObject
*>
*)metadataObjects
fromConnection:(AVCaptureConnection
*)connection
{
if
(metadataObjects
!=
nil
&&
metadataObjects.count
>
0)
{
AVMetadataMachineReadableCodeObject
*metadataObject
=
metadataObjects.firstObject;
if
([[metadataObject
type]
isEqualToString:AVMetadataObjectTypeQRCode])
{
NSString
*message
=
[metadataObject
stringValue];
[self.label
performSelectorOnMainThread:@selector(setText:)
withObject:message
waitUntilDone:NO];
}
}
}需要提醒的是,由于AVCaptureMetaDataOutput对象代理的设置,该代理方法会在setMetadataObjectsDelegate:queue:指定的队列上调用,如果需要更新用户界面,则必须在主线程中进行。2应用示例下面,我们就做一个如下图所示的二维码阅读器:
其中主要实现的功能有:通过摄像头实时扫描并读取二维码。解析从相册中选择的二维码图片。由于二维码的扫描是基于实时的视频捕获,因此相关的操作无法在模拟器上进行测试,也不能在没有相机的设备上进行测试。如果想要查看该应用,需要连接自己的iPhone设备来运行。2.1创建项目打开Xcode,创建一个新的项目(File\New\Project...),选择iOS一栏下的Application中的SingleViewApplication模版,然后点击Next,填写项目选项。在ProductName中填写QRCodeReaderDemo,选择Objective-C语言,点击Next,选择文件位置,并单击Create创建项目。2.2构建界面打开Main.storyboard文件,在当前控制器中嵌入导航控制器,并添加标题QRCodeReader:
在视图控制器中添加ToolBar、FlexibleSpaceBarButtonItem、BarButtonItem、View,布局如下:
其中,各元素及作用:ToolBar:添加在控制器视图的最底部,其BarItem标题为Start,具有双重作用,用于启动和停止扫描。FlexibleSpaceBarButtonItem:分别添加在Start的左右两侧,用于固定Start的位置使其居中显示。BarButtonItem:添加在导航栏的右侧,标题为Album,用于从相册选择二维码图片进行解析。View:添加在控制器视图的中间,用于稍后设置扫描框。在这里使用自动布局固定宽高均为260,并且水平和垂直方向都是居中。创建一个名为ScanView的新文件(File\New\File…),它是UIView的子类。然后选中视图控制器中间添加的View,将该视图的类名更改为ScanView:
打开辅助编辑器,将storyboard中的元素连接到代码中:
注意,需要在ViewController.m文件中导入ScanView.h文件。2.3添加代码2.3.1扫描二维码首先在ViewController.h文件中导入AVFoundation框架:#import
<AVFoundation/AVFoundation.h>切换到ViewController.m文件,添加AVCaptureMetadataOutputObjectsDelegate协议,并在接口部分添加下面的属性:@interface
ViewController
()<AVCaptureMetadataOutputObjectsDelegate>
//
properties
@property
(assign,
nonatomic)
BOOL
isReading;
@property
(strong,
nonatomic)
AVCaptureSession
*captureSession;
@property
(strong,
nonatomic)
AVCaptureVideoPreviewLayer
*previewLayer;在viewDidLoad方法中添加下面代码:-
(void)viewDidLoad
{
[super
viewDidLoad];
self.isReading
=
NO;
self.captureSession
=
nil;
}然后在实现部分添加startScanning方法和stopScanning方法及相关代码:-
(void)startScanning
{
self.captureSession
=
[[AVCaptureSession
alloc]
init];
//
add
input
NSError
*error;
AVCaptureDevice
*device
=
[AVCaptureDevice
defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput
*deviceInput
=
[[AVCaptureDeviceInput
alloc]
initWithDevice:device
error:&error];
if
(!deviceInput)
{
NSLog(@"%@",
[error
localizedDescription]);
}
[self.captureSession
addInput:deviceInput];
//
add
output
AVCaptureMetadataOutput
*metadataOutput
=
[[AVCaptureMetadataOutput
alloc]
init];
[self.captureSession
addOutput:metadataOutput];
//
configure
output
dispatch_queue_t
queue
=
dispatch_queue_create("MyQueue",
NULL);
[metadataOutput
setMetadataObjectsDelegate:self
queue:queue];
[metadataOutput
setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
//
configure
previewLayer
self.previewLayer
=
[[AVCaptureVideoPreviewLayer
alloc]
initWithSession:self.captureSession];
[self.previewLayer
setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[self.previewLayer
setFrame:self.view.bounds];
[self.view.layer
addSublayer:self.previewLayer];
//
start
scanning
[self.captureSession
startRunning];
}
-
(void)stopScanning
{
[self.captureSession
stopRunning];
self.captureSession
=
nil;
[self.previewLayer
removeFromSuperlayer];
}找到startStopAction:并在该方法中调用上面的方法:-
(IBAction)startStopAction:(id)sender
{
if
(!self.isReading)
{
[self
startScanning];
[self.view
bringSubviewToFront:self.toolBar];
[self.startStopButton
setTitle:@"Stop"];
}
else
{
[self
stopScanning];
[self.startStopButton
setTitle:@"Start"];
}
self.isReading
=
!self.isReading;
}至此,二维码扫描相关的代码已经完成,如果想要它能够正常运行的话,还需要在Info.plist文件中添加NSCameraUsageDescription键及相应描述以访问相机:
需要注意的是,现在只能扫描二维码但是还不能读取到二维码中的内容,不过我们可以连接设备,运行试下:2.3.2读取二维码读取二维码需要实现AVCaptureMetadataOutputObjectsDelegate协议的captureOutput:didOutputMetadataObjects:fromConnection:方法:-
(void)captureOutput:(AVCaptureOutput
*)output
didOutputMetadataObjects:(NSArray<__kindof
AVMetadataObject
*>
*)metadataObjects
fromConnection:(AVCaptureConnection
*)connection
{
if
(metadataObjects
!=
nil
&&
metadataObjects.count
>
0)
{
AVMetadataMachineReadableCodeObject
*metadataObject
=
metadataObjects.firstObject;
if
([[metadataObject
type]
isEqualToString:AVMetadataObjectTypeQRCode])
{
NSString
*message
=
[metadataObject
stringValue];
[self
performSelectorOnMainThread:@selector(displayMessage:)
withObject:message
waitUntilDone:NO];
[self
performSelectorOnMainThread:@selector(stopScanning)
withObject:nil
waitUntilDone:NO];
[self.startStopButton
performSelectorOnMainThread:@selector(setTitle:)
withObject:@"Start"
waitUntilDone:NO];
self.isReading
=
NO;
}
}
}
-
(void)displayMessage:(NSString
*)message
{
UIViewController
*vc
=
[[UIViewController
alloc]
init];
UITextView
*textView
=
[[UITextView
alloc]
initWithFrame:vc.view.bounds];
[textView
setText:message];
[textView
setFont:[UIFont
preferredFontForTextStyle:UIFontTextStyleBody]];
textView.editable
=
NO;
[vc.view
addSubview:textView];
[self.navigationController
showViewController:vc
sender:nil];
}在这里我们将扫码结果显示在一个新的视图中,如果你运行程序的话应该可以看到扫描的二维码内容了。另外,为了使我们的应用更逼真,可以在扫描到二维码信息时让它播放声音。这首先需要在项目中添加一个音频文件:
然后在接口部分添加一个AVAudioPlayer对象的属性:@property
(strong,
nonatomic)
AVAudioPlayer
*audioPlayer;在实现部分添加loadSound方法及代码,并在viewDidLoad中调用该方法:-
(void)loadSound
{
NSString
*soundFilePath
=
[[NSBundle
mainBundle]
pathForResource:@"beep"
ofType:@"mp3"];
NSURL
*soundURL
=
[NSURL
URLWithString:soundFilePath];
NSError
*error;
self.audioPlayer
=
[[AVAudioPlayer
alloc]
initWithContentsOfURL:soundURL
error:&error];
if
(error)
{
NSLog(@"Could
not
play
sound
file.");
NSLog(@"%@",
[error
localizedDescription]);
}
else
{
[self.audioPlayer
prepareToPlay];
}
}
-
(void)viewDidLoad
{
...
[self
loadSound];
}最后,在captureOutput:didOutputMetadataObjects:fromConnection:方法中添加下面的代码来播放声音:-
(void)captureOutput:(AVCaptureOutput
*)output
didOutputMetadataObjects:(NSArray<__kindof
AVMetadataObject
*>
*)metadataObjects
fromConnection:(AVCaptureConnection
*)connection
{
if
(metadataObjects
!=
nil
&&
metadataObjects.count
>
0)
{
AVMetadataMachineReadableCodeObject
*metadataObject
=
metadataObjects.firstObject;
if
([[metadataObject
type]
isEqualToString:AVMetadataObjectTypeQRCode])
{
...
self.isReading
=
NO;
//
play
sound
if
(self.audioPlayer)
{
[self.audioPlayer
play];
}
}
}2.3.3设置扫描框目前点击Start按钮,整个视图范围都可以扫描二维码。现在,我们需要设置一个扫描框,以限制只有扫描框区域内的二维码被读取。在这里,将扫描区域设置为storyboard中添加的视图,即scanView。在实现部分找到startReading方法,添加下面的代码:-
(void)startScanning
{
//
configure
previewLayer
...
//
set
the
scanning
area
[[NSNotificationCenter
defaultCenter]
addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification
object:nil
queue:[NSOperationQueue
mainQueue]
usingBlock:^(NSNotification
*
_Nonnull
note)
{
metadataOutput.rectOfInterest
=
[self.previewLayer
metadataOutputRectOfInterestForRect:self.scanView.frame];
}];
//
start
scanning
...
}需要注意的是,rectOfInterest属性不能在设置metadataOutput时直接设置,而需要在AVCaptureInputPortFormatDescriptionDidChangeNotification通知里设置,否则metadataOutputRectOfInterestForRect:方法会返回(0,0,0,0)。为了让扫描框更真实的显示,我们需要自定义ScanView,为其绘制边框、四角以及扫描线。首先打开ScanView.m文件,在实现部分重写initWithCoder:方法,为scanView设置透明的背景颜色:-
(instancetype)initWithCoder:(NSCoder
*)aDecoder
{
self
=
[super
initWithCoder:aDecoder];
if
(self)
{
self.backgroundColor
=
[UIColor
clearColor];
}
return
self;
}然后重写drawRect:方法,为scanView绘制边框和四角:-
(void)drawRect:(CGRect)rect
{
CGContextRef
context
=
UIGraphicsGetCurrentContext();
//
绘制白色边框
CGContextAddRect(context,
self.bounds);
CGContextSetStrokeColorWithColor(context,
[UIColor
whiteColor].CGColor);
CGContextSetLineWidth(context,
2.0);
CGContextStrokePath(context);
//
绘制四角:
CGContextSetStrokeColorWithColor(context,
[UIColor
greenColor].CGColor);
CGContextSetLineWidth(context,
5.0);
//
左上角:
CGContextMoveToPoint(context,
0,
30);
CGContextAddLineToPoint(context,
0,
0);
CGContextAddLineToPoint(context,
30,
0);
CGContextStrokePath(context);
//
右上角:
CGContextMoveToPoint(context,
self.bounds.size.width
-
30,
0);
CGContextAddLineToPoint(context,
self.bounds.size.width,
0);
CGContextAddLineToPoint(context,
self.bounds.size.width,
30);
CGContextStrokePath(context);
//
右下角:
CGContextMoveToPoint(context,
self.bounds.size.width,
self.bounds.size.height
-
30);
CGContextAddLineToPoint(context,
self.bounds.size.width,
self.bounds.size.height);
CGContextAddLineToPoint(context,
self.bounds.size.width
-
30,
self.bounds.size.height);
CGContextStrokePath(context);
//
左下角:
CGContextMoveToPoint(context,
30,
self.bounds.size.height);
CGContextAddLineToPoint(context,
0,
self.bounds.size.height);
CGContextAddLineToPoint(context,
0,
self.bounds.size.height
-
30);
CGContextStrokePath(context);
}如果希望在扫描过程中看到刚才绘制的扫描框,还需要切换到ViewController.m文件,在startStopAction:方法中添加下面的代码来显示扫描框:-
(IBAction)startStopAction:(id)sender
{
if
(!self.isReading)
{
...
[self.view
bringSubviewToFront:self.toolBar];
//
display
toolBar
[self.view
bringSubviewToFront:self.scanView];
//
display
scanView
...
}
...
}现在运行,你会看到下面的效果:
接下来我们继续添加扫描线。首先在ScanView.h文件的接口部分声明一个NSTimer对象的属性:@property
(nonatomic,
strong)
NSTimer
*timer;然后切换到ScanView.m文件,在实现部分添加loadScanLine方法及代码,并在initWithCoder:方法中调用:-
(void)loadScanLine
{
self.timer
=
[NSTimer
scheduledTimerWithTimeInterval:3.0
repeats:YES
block:^(NSTimer
*
_Nonnull
timer)
{
UIView
*lineView
=
[[UIView
alloc]
initWithFrame:CGRectMake(0,
0,
self.bounds.size.width,
1.0)];
lineView.backgroundColor
=
[UIColor
greenColor];
[self
addSubview:lineView];
[UIView
animateWithDuration:3.0
animations:^{
lineView.frame
=
CGRectMake(0,
self.bounds.size.height,
self.bounds.size.width,
2.0);
}
completion:^(BOOL
finished)
{
[lineView
removeFromSuperview];
}];
}];
}
-
(instancetype)initWithCoder:(NSCoder
*)aDecoder
{
...
if
(self)
{
...
[self
loadScanLine];
}
...
}然后切换到ViewController.m文件,在startStopAction:方法中添加下面代码以启用和暂停计时器:-
(IBAction)startStopAction:(id)sender
{
if
(!self.isReading)
{
...
[self.view
bringSubviewToFront:self.scanView];
//
display
scanView
self.scanView.timer.fireDate
=
[NSDate
distantPast];
//start
timer
...
}
else
{
[self
stopScanning];
self.scanView.timer.fireDate
=
[NSDate
distantFuture];
//stop
timer
...
}
...
}最后,再在viewWillAppear:的重写方法中添加下面代码:-
(void)viewWillAppear:(BOOL)animated
{
[super
viewWillAppear:animated];
self.scanView.timer.fireDate
=
[NSDate
distantFuture];
}可以运行看下:2.3.4从图片解析二维码从iOS8开始,可以使用CoreImage框架中的CIDetector解析图片中的二维码。在这个应用中,我们通过点击Album按钮,从相册选取二维码来解析。在写代码之前,需要在Info.plist文件中添加NSPhotoLibraryAddUsageDescription键及相应描述以访问相册:
然后在ViewController.m文件中添加UIImagePicker
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年增强填充剂市场分析及竞争策略报告
- 热力发电厂机炉电考试:机炉电考专业篇考点巩固(题库版)
- 热工程控保护考试考试试题(三)
- 全科医学住院医师:内科主要疾病试题预测(强化练习)
- 2024年软膏剂机械行业企业战略风险管理报告
- 2024年大、中容量数字程控交换机项目招商引资报告
- 2024年电池隔膜项目调研分析报告
- 2024年阿米妥项目经营分析报告
- 2024年脑血管病用药项目策划方案报告
- 广州市番禹区重点中学2022年中考化学押题试卷含解析
- 九年级下册第十单元酸和碱黄琳娜-微项目《皮蛋制作》-酸碱盐的复习
- 2023年泰州东部新城发展集团有限公司招聘笔试题库及答案解析
- 英语听说课教学方法课件
- 命题作文《这就是幸福》指导与讲评讲解课件
- 2022年中国盐业集团有限公司校园招聘笔试试题及答案解析
- 公司员工培训管理办法
- 转子动力学基础课件
- 碎煤机大修施工方案
- 三只松鼠品牌网络营销策略问题研究分析 市场营销专业
- BRCGS食品安全全球标准第9版管理手册及全套程序文件
评论
0/150
提交评论