zoukankan      html  css  js  c++  java
  • 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:0];
        
        // 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 = 3;
        
        [self.view addSubview:boxView];
        
        // 设置扫描线
        CALayer *scanLayer = [[CALayer alloc] init];
        self.scanLayer = scanLayer;
        
        scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2);
        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 = 3;
        
        [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:0];
        
        // 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 = 3;
        
        [self.view addSubview:boxView];
        
        // 设置扫描线
        CALayer *scanLayer = [[CALayer alloc] init];
        self.scanLayer = scanLayer;
        
        scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2);
        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 = -20; self.scanLayer.frame = frame; }else{ frame.origin.y += 3; 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
  • 相关阅读:
    MS CRM 2011 C#中获取Web Resource
    MS CRM 2011 Form与Web Resource在JScript中的相互调用
    MS CRM 2011 JScript操作lookup control
    MS CRM 2011 导出Ribbon的定义
    MS CRM 2011 为64位Outlook安装Silverlight
    MS CRM 2011 JScript getValue 与 setValue方法
    MS CRM 2011 在JScript中同步和异步使用REST Endpoint
    C#实现全局快捷键(系统热键)响应(转)
    程序隐藏启动的C#实现经验
    WinForm程序启动时不显示主窗体
  • 原文地址:https://www.cnblogs.com/Mr-Ygs/p/4904710.html
Copyright © 2011-2022 走看看