ios7 苹果原生二维码扫描(和微信类似)
在ios7苹果推出了二维码扫描,以前想要做二维码扫描,只能通过第三方ZBar与ZXing。
ZBar在扫描的灵敏度上,和内存的使用上相对于ZXing上都是较优的,但是对于 “圆角二维码” 的扫描确很困难。
ZXing 是 Google Code上的一个开源的条形码扫描库,是用java设计的,连Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了c++ port. Github上的Objectivc-C port,其实就是用OC代码封装了一下而已,而且已经停止维护。这样效率非常低,在instrument下面可以看到CPU和内存疯涨,在内存小的机器上很容易崩溃。
AVFoundation无论在扫描灵敏度和性能上来说都是最优的。
首先要导入#import <AVFoundation/AVFoundation.h>框架
其次还需要授权应用可以访问相机
// 判断相机是否授权使用相机
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(status == AVAuthorizationStatusAuthorized) { } else if(status == AVAuthorizationStatusDenied){
// NSLog(@"denied不允许");
return ;
} else if(status == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
// NSLog(@"允许");
} else {
// NSLog(@"不允许");
return;
}
}];
} // typedef enum
// AVAuthorizationStatusNotDetermined = 0, // 用户尚未做出选择这个应用程序的问候
// AVAuthorizationStatusRestricted, // 此应用程序没有被授权访问的照片数据。可能是家长控制权限
// AVAuthorizationStatusDenied, // 用户已经明确否认了这一照片数据的应用程序访问
// AVAuthorizationStatusAuthorized // 用户已经授权应用访问照片数据} CLAuthorizationStatus;
完成二维码扫描大致有十个步骤:
// 1.获取输入设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 2.创建输入对象
NSError *error;
AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error]; if (inPut == nil) {
UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"设备不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[self.view addSubview:aler];
[aler show];
return;
} // 3.创建输出对象
AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init]; // 4.设置代理监听输出对象的输出流 (说明:使用主线程队列,相应比较同步,使用其他队列,相应不同步,容易让用户产生不好的体验)
[outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 5.创建会话
AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.session = session; // 6.将输入和输出对象添加到会话
if ([session canAddInput:inPut]) {
[session addInput:inPut];
}
if ([session canAddOutput:outPut]) {
[session addOutput:outPut];
} // 7.告诉输出对象, 需要输出什么样的数据 // 提示:一定要先设置会话的输出为output之后,再指定输出的元数据类型!
[outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; // 8.创建预览图层
AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
preViewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:preViewLayer atIndex:]; // 9.设置扫面范围
outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5); // 10.设置扫描框
UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
self.boxView = boxView; boxView.layer.borderColor = [UIColor yellowColor].CGColor;
boxView.layer.borderWidth = ; [self.view addSubview:boxView]; // 设置扫描线
CALayer *scanLayer = [[CALayer alloc] init];
self.scanLayer = scanLayer; scanLayer.frame = CGRectMake(, , boxView.bounds.size.width, );
scanLayer.backgroundColor = [UIColor redColor].CGColor;
[boxView.layer addSublayer:scanLayer]; // 开始扫描
[session startRunning];
其中第9个步骤是可以优化内存的
@property(nonatomic) CGRect rectOfInterest;
这个属性大致意思就是告诉系统它需要注意的区域,大部分APP的扫码UI中都会有一个框,提醒你将条形码放入那个区域,这个属性的作用就在这里,它可以设置一个范围,只处理在这个范围内捕获到的图像的信息。如此一来,我们代码的效率又会得到很大的提高,在使用这个属性的时候。需要几点注意:
1、这个CGRect参数和普通的Rect范围不太一样,它的四个值的范围都是0-1,表示比例。
2、经过测试发现,这个参数里面的x对应的恰恰是距离左上角的垂直距离,y对应的是距离左上角的水平距离。
3、宽度和高度设置的情况也是类似。
/// 经过测试 使用rectOfInterest 更改扫描范围 并没有很好的可控制范围,如果想达到想微信那样,只有在固定的扫描框中才可以扫描成功
可以使用以下设置,在
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection; 方法中,判断二维码的三个坐标点是否在扫描框中。
for (id objects in metadataObjects) {
// 判断检测到的对象类型
if (![objects isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
return;
}
// 转换对象坐标
AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[preViewLayer transformedMetadataObjectForMetadataObject:objects];
// 判断扫描范围
if (!CGRectContainsRect(self.boxView.frame, obj.bounds)) {
continue;
}
}
-----------------------------以下是源码:
#import "ScanQrcodeVController.h"
@protocol ScanQrcodeVControllerDelegate <NSObject>
// 二维码返回结果
-(void)scanQrcodeWithNString:(NSString *) ruselt;
@end
@interface ScanQrcodeVController : UIViewController
@property (nonatomic, weak) id<ScanQrcodeVControllerDelegate>delegate;
@end
#import "ScanQrcodeVController.m"
@interface ScanQrcodeVController ()<AVCaptureMetadataOutputObjectsDelegate>
// 会话
@property (nonatomic, strong) AVCaptureSession *session;
// 定时器
@property (nonatomic, strong) CADisplayLink *link;
// 扫描线
@property (nonatomic, strong) CALayer *scanLayer;
// 扫描框
@property (nonatomic, weak) UIView *boxView;
/// 保存二维码结果
@property (nonatomic, copy) NSString *string;
@end @implementation ScanQrcodeVController
- (void)viewDidLoad {
[super viewDidLoad]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"NavBack"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"确定" style:UIBarButtonItemStylePlain target:self action:@selector(doneClick)]; [self scanCode];
} -(void)scanCode { CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataFrame)];
self.link = link;
link.frameInterval = ; [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
// 判断相机是否授权使用相机
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(status == AVAuthorizationStatusAuthorized) {
} else if(status == AVAuthorizationStatusDenied){
// NSLog(@"denied不允许");
return ;
} else if(status == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
// NSLog(@"允许");
} else {
// NSLog(@"不允许");
return;
}
}];
// 1.获取输入设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2.创建输入对象
NSError *error;
AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
if (inPut == nil) {
UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"设备不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[self.view addSubview:aler];
[aler show];
return;
}
// 3.创建输出对象
AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
// 4.设置代理监听输出对象的输出流 说明:使用主线程队列,相应比较同步,使用其他队列,相应不同步,容易让用户产生不好的体验
[outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 5.创建会话
AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.session = session;
// 6.将输入和输出对象添加到会话
if ([session canAddInput:inPut]) {
[session addInput:inPut];
}
if ([session canAddOutput:outPut]) {
[session addOutput:outPut];
}
// 7.告诉输出对象, 需要输出什么样的数据 // 提示:一定要先设置会话的输出为output之后,再指定输出的元数据类型!
[outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
// 8.创建预览图层
AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
preViewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:preViewLayer atIndex:];
// 9.设置扫面范围
outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
// 10.设置扫描框
UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
self.boxView = boxView;
boxView.layer.borderColor = [UIColor yellowColor].CGColor;
boxView.layer.borderWidth = ;
[self.view addSubview:boxView];
// 设置扫描线
CALayer *scanLayer = [[CALayer alloc] init];
self.scanLayer = scanLayer;
scanLayer.frame = CGRectMake(, , boxView.bounds.size.width, );
scanLayer.backgroundColor = [UIColor redColor].CGColor;
[boxView.layer addSublayer:scanLayer];
// 开始扫描
[session startRunning];
}
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
for (id objects in metadataObjects) {
// 判断检测到的对象类型
if (![objects isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
return;
}
// 转换对象坐标
AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[preViewLayer transformedMetadataObjectForMetadataObject:objects];
// 判断扫描范围
if (!CGRectContainsRect(self.boxView.frame, obj.bounds)) {
continue;
}
// 设置代理
if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) {
[self.delegate scanQrcodeWithNString:obj.stringValue];
}
// 停止扫描
[self.session stopRunning];
// 移除CADisplayLink对象
[self.link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.link = nil;
}
} -(void)updataFrame { CGRect frame = self.scanLayer.frame; if (self.scanLayer.frame.origin.y > self.boxView.frame.size.height) {
frame.origin.y = -;
self.scanLayer.frame = frame;
}else{
frame.origin.y += ;
self.scanLayer.frame = frame;
} } -(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
// 记得释放CADisplayLink对象
if (self.link != nil) {
[self.link invalidate];
self.link = nil;
}
} // 返回上一个界面
-(void)goBack { [self.navigationController popViewControllerAnimated:YES];
} // 二维码扫描完成
-(void)doneClick { // 设置代理
if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) {
[self.delegate scanQrcodeWithNString:self.string];
} [self.navigationController popToRootViewControllerAnimated:YES];
}
@end
ios7 苹果原生二维码扫描(和微信类似)的更多相关文章
- iOS:原生二维码扫描
做iOS的二维码扫描,有两个第三方库可以选择,ZBar和ZXing.今天要介绍的是iOS7.0后AVFoundation框架提供的原生二维码扫描. 首先需要添加AVFoundation.framewo ...
- Firemonkey 原生二维码扫描优化
之前用了ZXing的Delphi版本,运行自带的例子,速度非常慢,与安卓版本的相比查了很多,因此打算使用集成jar的方法,但是总觉得美中不足. 经过一番研究,基本上解决了问题. 主要有两方面的优化: ...
- iOS 原生二维码扫描和生成
代码地址如下:http://www.demodashi.com/demo/12551.html 一.效果预览: 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描工 ...
- iOS 原生二维码扫描(可限制扫描区域)
篇文章的主要原因不是展示如何使用 AVFoundation 来进行二维码扫描,更主要的是限制扫描二维码的范围.(因为默认的是全屏扫描) 项目遇到扫描二维码的功能需求,这里我放弃了使用三方库,而采用了 ...
- 【转】 iOS 原生二维码扫描(可限制扫描区域)
在用 AVFoundation 完成扫码后,遇到2个问题: 1,如何限制扫描范围? 2.条形码如何扫描? 一位朋友的文章帮助了我,特地转来,可以帮到有需要的朋友. 原文:http://blog.csd ...
- iOS 原生二维码扫描,带扫描框和扫描过程动画
在代码中使用了相对布局框架Masonry 准备两张图片,一张是扫描边框,一张是扫描时的细线分别命名 scanFrame.png和scanLine.png并提前放入工程 导入相对布局头文件 #defin ...
- ipad开发:二维码扫描,摄像头旋转角度问题解决办法
之前一直是在手机上开发,用系统原生二维码扫描功能,一点问题都没有,但是在ipad上,用户是横屏操作的,虽然界面旋转了,是横屏的,但是摄像头里显示的依然是竖屏效果,也就是说从摄像头里看到的和人眼看到的内 ...
- iOS系统原生 二维码的生成、扫描和读取(高清、彩色)
由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上.连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付.可见现在的二维 ...
- iOS学习——iOS原生实现二维码扫描
最近项目上需要开发扫描二维码进行签到的功能,主要用于开会签到的场景,所以为了避免作弊,我们再开发时只采用直接扫描的方式,并且要屏蔽从相册读取图片,此外还在二维码扫描成功签到时后台会自动上传用户的当前地 ...
随机推荐
- 英语之路 zt
各位为英语而郁闷的兄弟姐妹们: 自从考完GRE和TOEFL以后,心有所感,本想写点心得,但是因为太懒没写成.今日风雨如晦,心中又有所感,于是一舒笔墨,写下我学英语的方法.俺知道有很多兄弟姐妹们和曾经的 ...
- HDU-- Buy Tickets
告知每次要插到第 i 个位置上,问最后它们的顺序是什么. 这一题,不是考线段树,是考如何想出用线段树...思维很巧妙,倒过来做的话就能确定此人所在的位置.... Buy Tickets Time ...
- 将access数据转换成oracle数据
1.打开access数据文件,选择需转换的表/导出/ODBC数据库 2.输入导出的表名 3.选择数据源,点击新建 4.选择数据源驱动程序 5.保存DSN文件 6.下一步,输入oracle的SERVIC ...
- Mac下快捷键列表
主界面 command + tab 切换程序 command + ` 在程序内切换界面 command + w 关闭界面 command + q 关闭程序 command + option + esc ...
- Genotype&&陨石的秘密
Genotype: Genotype 是一个有限的基因序列.它是由大写的英文字母A-Z组成,不同的字母表示不同种类的基因.一个基因可以分化成为一对新的基因.这种分化被一个定义的规则集合所控制.每个分化 ...
- Ubuntu 如何使用apt命令安装、升级、卸载软件
apt-get是一条linux命令,适用于deb包管理式的操作系统,主要用于自动从互联网的软件仓库中搜索.安装.升级.卸载软件或操作系统. apt-get命令一般需要root权限执行,所以一般跟着su ...
- 借助 AOP 为 Java Web 应用记录性能数据
作为开发者,应用的性能始终是我们最感兴趣的话题之一.然而,不是所有的开发者都对自己维护的应用的性能有所了解,更别说快速定位性能瓶颈并实施解决方案了. 今年北京 Velocity 的赞助商大多从事 AP ...
- 1116 HTML CSS
1. JPEG和GIF在使用时的注意事项: JPEG可以连续调次(复制品中有中间层次,如照片)图像中获得最好效果:JPEG可以用1600万色显示图像,是有损格式,不支持透明. GIF适用于几种纯色组成 ...
- iOS新建项目基本配置
项目整体同xib+代码的方式 1.调整项目文件结构 2.将资源图片导入工程 General->LaunchScreen 修改 3.App名称修改 info->Bundle name 4.删 ...
- Android源代码之Gallery专题研究(1)
前言 时光飞逝,从事Android系统开发已经两年了,总想写点什么来安慰自己.思考了非常久总是无法下笔,认为没什么好写的.如今最终决定写一些符合大多数人需求的东西,想必使用过Android手机的人们一 ...