在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>框架

其次还需要授权应用可以访问相机

  1. // 判断相机是否授权使用相机
  2. AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  3. if(status == AVAuthorizationStatusAuthorized) {
  4.  
  5. } else if(status == AVAuthorizationStatusDenied){
  6. // NSLog(@"denied不允许");
  7. return ;
  8. } else if(status == AVAuthorizationStatusNotDetermined){
  9. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  10. if(granted){
  11. // NSLog(@"允许");
  12. } else {
  13. // NSLog(@"不允许");
  14. return;
  15. }
  16. }];
  17. }
  18.  
  19. // typedef enum
  20. // AVAuthorizationStatusNotDetermined = 0, // 用户尚未做出选择这个应用程序的问候
  21. // AVAuthorizationStatusRestricted, // 此应用程序没有被授权访问的照片数据。可能是家长控制权限
  22. // AVAuthorizationStatusDenied, // 用户已经明确否认了这一照片数据的应用程序访问
  23. // AVAuthorizationStatusAuthorized // 用户已经授权应用访问照片数据} CLAuthorizationStatus;

完成二维码扫描大致有十个步骤:

  1. // 1.获取输入设备
  2. AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  3.  
  4. // 2.创建输入对象
  5. NSError *error;
  6. AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
  7.  
  8. if (inPut == nil) {
  9. UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"设备不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
  10. [self.view addSubview:aler];
  11. [aler show];
  12. return;
  13. }
  14.  
  15. // 3.创建输出对象
  16. AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
  17.  
  18. // 4.设置代理监听输出对象的输出流 (说明:使用主线程队列,相应比较同步,使用其他队列,相应不同步,容易让用户产生不好的体验)
  19. [outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  20.  
  21. // 5.创建会话
  22. AVCaptureSession *session = [[AVCaptureSession alloc] init];
  23. self.session = session;
  24.  
  25. // 6.将输入和输出对象添加到会话
  26. if ([session canAddInput:inPut]) {
  27. [session addInput:inPut];
  28. }
  29. if ([session canAddOutput:outPut]) {
  30. [session addOutput:outPut];
  31. }
  32.  
  33. // 7.告诉输出对象, 需要输出什么样的数据 // 提示:一定要先设置会话的输出为output之后,再指定输出的元数据类型!
  34. [outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
  35.  
  36. // 8.创建预览图层
  37. AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
  38. preViewLayer.frame = self.view.bounds;
  39. [self.view.layer insertSublayer:preViewLayer atIndex:];
  40.  
  41. // 9.设置扫面范围
  42. outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
  43.  
  44. // 10.设置扫描框
  45. UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
  46. self.boxView = boxView;
  47.  
  48. boxView.layer.borderColor = [UIColor yellowColor].CGColor;
  49. boxView.layer.borderWidth = ;
  50.  
  51. [self.view addSubview:boxView];
  52.  
  53. // 设置扫描线
  54. CALayer *scanLayer = [[CALayer alloc] init];
  55. self.scanLayer = scanLayer;
  56.  
  57. scanLayer.frame = CGRectMake(, , boxView.bounds.size.width, );
  58. scanLayer.backgroundColor = [UIColor redColor].CGColor;
  59. [boxView.layer addSublayer:scanLayer];
  60.  
  61. // 开始扫描
  62. [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; 方法中,判断二维码的三个坐标点是否在扫描框中。

  1. for (id objects in metadataObjects) {
  2. // 判断检测到的对象类型
  3.  
  4. if (![objects isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
  5.  
  6. return;
  7.  
  8. }
  9.  
  10. // 转换对象坐标
  11.  
  12. AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[preViewLayer transformedMetadataObjectForMetadataObject:objects];
  13.  
  14. // 判断扫描范围
  15.  
  16. if (!CGRectContainsRect(self.boxView.frame, obj.bounds)) {
  17.  
  18. continue;
  19.  
  20.      }
  21.  
  22. }

-----------------------------以下是源码:

#import "ScanQrcodeVController.h"

  1. @protocol ScanQrcodeVControllerDelegate <NSObject>
  2. // 二维码返回结果
  3. -(void)scanQrcodeWithNString:(NSString *) ruselt;
  4. @end
  5. @interface ScanQrcodeVController : UIViewController
  6. @property (nonatomic, weak) id<ScanQrcodeVControllerDelegate>delegate;
  7. @end

#import "ScanQrcodeVController.m"

  1. @interface ScanQrcodeVController ()<AVCaptureMetadataOutputObjectsDelegate>
  2. // 会话
  3. @property (nonatomic, strong) AVCaptureSession *session;
  4. // 定时器
  5. @property (nonatomic, strong) CADisplayLink *link;
  6. // 扫描线
  7. @property (nonatomic, strong) CALayer *scanLayer;
  8. // 扫描框
  9. @property (nonatomic, weak) UIView *boxView;
  10. /// 保存二维码结果
  11. @property (nonatomic, copy) NSString *string;
  12. @end
  13.  
  14. @implementation ScanQrcodeVController
  15. - (void)viewDidLoad {
  16. [super viewDidLoad];
  17.  
  18. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"NavBack"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)];
  19.  
  20. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"确定" style:UIBarButtonItemStylePlain target:self action:@selector(doneClick)];
  21.  
  22. [self scanCode];
  23. }
  24.  
  25. -(void)scanCode {
  26.  
  27. CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataFrame)];
  28. self.link = link;
  29. link.frameInterval = ;
  30.  
  31. [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
  32.  
  33.   

// 判断相机是否授权使用相机

  1.  

AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

  1.  

if(status == AVAuthorizationStatusAuthorized) {

  1.  

} else if(status == AVAuthorizationStatusDenied){

  1.  

// NSLog(@"denied不允许");

  1.  

return ;

  1.  

} else if(status == AVAuthorizationStatusNotDetermined){

  1.  

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {

  1.  

if(granted){

  1.  

//                NSLog(@"允许");

  1.  

} else {

  1.  

//                NSLog(@"不允许");

  1.  

return;

  1.  

}

  1.  

}];

  1.  

// 1.获取输入设备

  1. AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  2. // 2.创建输入对象
  3. NSError *error;
  4. AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
  5. if (inPut == nil) {
  6. UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"设备不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
  7. [self.view addSubview:aler];
  8. [aler show];
  9. return;
  10. }
  11. // 3.创建输出对象
  12. AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
  13. // 4.设置代理监听输出对象的输出流 说明:使用主线程队列,相应比较同步,使用其他队列,相应不同步,容易让用户产生不好的体验
  14. [outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  15. // 5.创建会话
  16. AVCaptureSession *session = [[AVCaptureSession alloc] init];
  17. self.session = session;
  18. // 6.将输入和输出对象添加到会话
  19. if ([session canAddInput:inPut]) {
  20. [session addInput:inPut];
  21. }
  22. if ([session canAddOutput:outPut]) {
  23. [session addOutput:outPut];
  24. }
  25. // 7.告诉输出对象, 需要输出什么样的数据 // 提示:一定要先设置会话的输出为output之后,再指定输出的元数据类型!
  26. [outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
  27. // 8.创建预览图层
  28. AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
  29. preViewLayer.frame = self.view.bounds;
  30. [self.view.layer insertSublayer:preViewLayer atIndex:];
  31. // 9.设置扫面范围
  32. outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
  33. // 10.设置扫描框
  34. UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
  35. self.boxView = boxView;
  36. boxView.layer.borderColor = [UIColor yellowColor].CGColor;
  37. boxView.layer.borderWidth = ;
  38. [self.view addSubview:boxView];
  39. // 设置扫描线
  40. CALayer *scanLayer = [[CALayer alloc] init];
  41. self.scanLayer = scanLayer;
  42. scanLayer.frame = CGRectMake(, , boxView.bounds.size.width, );
  43. scanLayer.backgroundColor = [UIColor redColor].CGColor;
  44. [boxView.layer addSublayer:scanLayer];
  45. // 开始扫描
  46. [session startRunning];
  47. }
  48. -(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;

     }

  1.  

// 设置代理

      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;

}


  1. }
  2. -(void)updataFrame {
  3. CGRect frame = self.scanLayer.frame;
  4. if (self.scanLayer.frame.origin.y > self.boxView.frame.size.height) {
  5. frame.origin.y = -;
  6. self.scanLayer.frame = frame;
  7. }else{
  8. frame.origin.y += ;
  9. self.scanLayer.frame = frame;
  10. }
  11. }
  12. -(void)viewDidDisappear:(BOOL)animated{
  13. [super viewDidDisappear:animated];
  14. // 记得释放CADisplayLink对象
  15. if (self.link != nil) {
  16. [self.link invalidate];
  17. self.link = nil;
  18. }
  19. }
  20. // 返回上一个界面
  21. -(void)goBack {
  22. [self.navigationController popViewControllerAnimated:YES];
  23. }
  24. // 二维码扫描完成
  25. -(void)doneClick {
  26. // 设置代理
  27. if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) {
  28. [self.delegate scanQrcodeWithNString:self.string];
  29. }
  30. [self.navigationController popToRootViewControllerAnimated:YES];
  31. }
  32. @end

ios7 苹果原生二维码扫描(和微信类似)的更多相关文章

  1. iOS:原生二维码扫描

    做iOS的二维码扫描,有两个第三方库可以选择,ZBar和ZXing.今天要介绍的是iOS7.0后AVFoundation框架提供的原生二维码扫描. 首先需要添加AVFoundation.framewo ...

  2. Firemonkey 原生二维码扫描优化

    之前用了ZXing的Delphi版本,运行自带的例子,速度非常慢,与安卓版本的相比查了很多,因此打算使用集成jar的方法,但是总觉得美中不足. 经过一番研究,基本上解决了问题. 主要有两方面的优化: ...

  3. iOS 原生二维码扫描和生成

    代码地址如下:http://www.demodashi.com/demo/12551.html 一.效果预览: 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描工 ...

  4. iOS 原生二维码扫描(可限制扫描区域)

    篇文章的主要原因不是展示如何使用  AVFoundation 来进行二维码扫描,更主要的是限制扫描二维码的范围.(因为默认的是全屏扫描) 项目遇到扫描二维码的功能需求,这里我放弃了使用三方库,而采用了 ...

  5. 【转】 iOS 原生二维码扫描(可限制扫描区域)

    在用 AVFoundation 完成扫码后,遇到2个问题: 1,如何限制扫描范围? 2.条形码如何扫描? 一位朋友的文章帮助了我,特地转来,可以帮到有需要的朋友. 原文:http://blog.csd ...

  6. iOS 原生二维码扫描,带扫描框和扫描过程动画

    在代码中使用了相对布局框架Masonry 准备两张图片,一张是扫描边框,一张是扫描时的细线分别命名 scanFrame.png和scanLine.png并提前放入工程 导入相对布局头文件 #defin ...

  7. ipad开发:二维码扫描,摄像头旋转角度问题解决办法

    之前一直是在手机上开发,用系统原生二维码扫描功能,一点问题都没有,但是在ipad上,用户是横屏操作的,虽然界面旋转了,是横屏的,但是摄像头里显示的依然是竖屏效果,也就是说从摄像头里看到的和人眼看到的内 ...

  8. iOS系统原生 二维码的生成、扫描和读取(高清、彩色)

    由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上.连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付.可见现在的二维 ...

  9. iOS学习——iOS原生实现二维码扫描

    最近项目上需要开发扫描二维码进行签到的功能,主要用于开会签到的场景,所以为了避免作弊,我们再开发时只采用直接扫描的方式,并且要屏蔽从相册读取图片,此外还在二维码扫描成功签到时后台会自动上传用户的当前地 ...

随机推荐

  1. php--常用的时间处理函数

    天地四方曰宇,往古来今曰宙 时间是世界的重要组成部分,不论花开花落,还是云卷云舒都有它的影子. 但它源起何处?又将去向何方?没人知道答案,也不需要答案,我们需要的只是一个相对的起点来标识时间,现今世界 ...

  2. HDOJ/HDU 2203 亲和串(简单的判断~Java的indexOf()方法秒)

    Problem Description 人随着岁数的增长是越大越聪明还是越大越笨,这是一个值得全世界科学家思考的问题,同样的问题Eddy也一直在思考,因为他在很小的时候就知道亲和串如何判断了,但是发现 ...

  3. HTML5 Canvas核心技术—图形、动画与游戏开发.pdf4

    CanvasRenderingContext2D对象中用于平移.旋转坐标系的方法 镜像 scale(1,-1)绘制垂直镜像:scale(-1,1)绘制水平镜像 自定义的坐标变换 transform() ...

  4. xcode7下开发ios9等相关问题记录

    1.默认使用HTTPS请求如果在Xcode 9之前使用的时http请求,那么在XCode 9上编译的App是不能联网的,会提示如下错误:App Transport Security has block ...

  5. Caffe 编译

    Compilation Now that you have the prerequisites, edit your Makefile.config to change the paths for y ...

  6. PC-修改IE 与 禁止修改IE

    1.注册表编辑器1.1在Windows启动后1.2点击“开始”1.3“运行”菜单项1.4在“打开”栏中键入“regedit”1.5然后按“确定”键 2.首页项2.1展开注册表到“HKEY_LOCAL_ ...

  7. Lottery - CodeForces 589I(水)

    题目大意:有N个球K个人,现在要给这N个球涂上K种颜色,要求使抽到球的不同颜色的概率一致(N确保是K的倍数),求出来至少要给多少个球重新涂上颜色. 分析:先求出来所有球的每种颜色的个数,然后不到平均数 ...

  8. iOS开发之状态栏UIStatusBar图标操作

    NSArray *subIcons = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] val ...

  9. The server does not support version 3.1 of the JEE Web module specification.

    使用MyEclipse2015打开MyEclipse2014编辑的项目,在服务器Tomcat 7部署时,提示"The server does not support version 3.1 ...

  10. LINUX下解决netstat查看TIME_WAIT状态过多问题

     来源:多3度热爱 的BLOG   查看连接某服务端口最多的的IP地址 netstat -nat |awk '{print $5}'|awk -F: '{print $1}'|sort|uniq -c ...