新浦京81707con > 功能介绍 > 使用AVFoundation读取二维码,原生二维码扫描

原标题:使用AVFoundation读取二维码,原生二维码扫描

浏览次数:171 时间:2020-03-29

前几日一大半的app都会好似此的三个职能就是二维码扫描,那么些效应算得上是四个广大的相助功用,那么如何来促成啊?这里作者来给我们详细介绍一下怎么着轻巧来兑现那么些作用。

二维码(Quick Response Code,简单的称呼QR Code)是由水平和垂直三个趋势上的线条设计而成的一种二维条形码(barcodeState of Qatar。能够编码网站、电话号码、文本等内容,能够存款和储蓄大批量的多寡新闻。自iOS 7以来,二维码的更改和读取只须求动用Core Image框架和AVFoundation框架就能够轻轻便松完结。在这里处,我们主要介绍二维码的读取。关于二维码的转移,能够查阅运用CIFilter生成二维码小说中的介绍。

1、QRCode

  • 在 iOS7 早先,在 iOS 中落到实处二维码和条形码扫描,大家所知的有,两大开源组件 ZBar 与 ZXing。iOS7 之后方可行使种类原生 API 生成二维码, iOS8 之后方可生成条形码, 系统暗中认可生成的水彩是墨玉米黄。

    • 1、ZBar 在扫描的灵敏度上,和内部存款和储蓄器的采纳上相对于 ZXing 上都以较优的,但是对于 “圆角二维码” 的扫视确很劳苦。
    • 2、ZXing 是 谷歌 Code 上的二个开源的条形码扫描库,是用 java 设计的,连 Google Glass 都在行使的。但有人为了追求越来越高效能以致可移植性,现身了 c port。Github 上的 Objectivc-C port,其实正是用 OC 代码封装了一晃罢了,何况已经告一段落维护。那样效用十分的低,在 instrument 下边能够见见 CPU 和内部存款和储蓄器疯涨,在内部存款和储蓄器小的机器上相当轻便崩溃。
    • 3、AVFoundation 无论在围观灵敏度和天性上来讲都以最优的,所以肯定大家理应切换到AVFoundation,需求合营 iOS 6 或事前的本子能够用 ZBar 或 ZXing 代替。
  • 在 iOS8 系统中使用相机必要在 Info.plist 中增加 Privacy - Camera Usage Description,并设置其值。使用相册供给在 Info.plist 中加多Privacy - Photo Library Usage Description,并安装其值。

    图片 1

  • 安分守己下图在 Info.plist 文件大校 Localization native development region 的值改为 China。就算不安装此项弹出的相册页面中展现的按键等为德语菜单。

    图片 2

率先,二维码扫描要用到相机,大家须要导入AVFoundation框架,举个例子:作者成立一个QRCodeScanViewController的调节器类,导入AVFoundation,如下:

1 二维码的读取

读取二维码相当于透过扫描二维码图像以获得其所蕴涵的数码信息。供给明白的是,任何条形码(包罗二维码)的扫视都是遵照录像访谈(video capture),由此需求利用AVFoundation框架。

举目四望二维码的长河即从拍戏头捕获二维码图像(input)到剖判出字符串内容(output)的进度,主借使经过AVCaptureSession对象来兑现的。该对象用于协和从输入到输出的数据流,在试行进度中,需求先将输入和输出加多到AVCaptureSession对象中,然后经过发送startRunningstopRunning新闻来运行或结束数据流,最终经过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之后才具安装要拍卖的元数据类型,不然汇合世上面包车型地铁大错特错:

    Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: [AVCaptureMetadataOutput setMetadataObjectTypes:] Unsupported type found - 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、系统原生二维码

#import "QRCodeScanViewController.h"#import <AVFoundation/AVFoundation.h>

2 应用示范

上边,我们就做叁个之类图所示的二维码阅读器:

当中主要实现的功能有:

  • 通过雕塑头实时扫描并读取二维码。
  • 解析从相册中精选的二维码图片。

是因为二维码的扫视是依照实时的录制捕获,由此相关的操作不能够在模拟器上开展测量检验,也无法在并没有相机的器具上海展览中心开测量检验。假若想要查看该选用,须要三回九转本身的黑莓设备来运转。

2.1 扫描二维码

  • 合法提供的接口很简单,间接看代码,首要行使的是 AVFoundation。

        // 包含头文件
        #import <AVFoundation/AVFoundation.h>
    
        // 遵守协议
        <AVCaptureMetadataOutputObjectsDelegate>
    
        // 输入输出的中间桥梁
        @property (nonatomic, strong) AVCaptureSession *session;
    
        // 扫描窗口
        @property (nonatomic, strong) UIImageView *scanView;
    
        // 创建扫描视图窗口,自定义方法
        - (void)createdScanView {
    
            CGFloat margin = 50;
            CGRect scanFrame = CGRectMake(margin,
                                          margin   20,
                                          self.view.bounds.size.width - margin * 2,
                                          self.view.bounds.size.width - margin * 2);
    
            self.scanView = [[UIImageView alloc] initWithFrame:scanFrame];
    
            self.scanView.image = [UIImage imageNamed:@"scan_bg2"];
    
            [self.view addSubview:self.scanView];
    
            // 创建扫描
            [self startScan];
        }
    
        // 创建扫描,自定义方法
        - (void)startScan {
    
            // 获取摄像设备
            AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
            // 创建输入流
            AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    
            // 创建输出流
            AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    
            // 设置代理,在主线程里刷新
            [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
            // 设置有效扫描区域
            output.rectOfInterest = [self getScanCropWithScanViewFrame:self.scanView.frame readerViewBounds:self.view.bounds];
    
            // 初始化链接对象
            self.session = [[AVCaptureSession alloc] init];
    
            // 设置采集率,高质量
            [self.session setSessionPreset:AVCaptureSessionPresetHigh];
    
            [self.session addInput:input];
            [self.session addOutput:output];
    
            // 设置扫码支持的编码格式(如下设置条形码和二维码兼容)
            output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,
                                           AVMetadataObjectTypeEAN13Code,
                                           AVMetadataObjectTypeEAN8Code,
                                           AVMetadataObjectTypeCode128Code];
    
            AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
            layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
            layer.frame = self.view.layer.bounds;
            [self.view.layer insertSublayer:layer atIndex:0];
    
            // 开始捕获
            [self.session startRunning];
        }
    
        // 设置扫描区域的比例关系,自定义方法
        - (CGRect)getScanCropWithScanViewFrame:(CGRect)scanViewFrame readerViewBounds:(CGRect)readerViewBounds {
    
            CGFloat x, y, width, height;
    
            x = scanViewFrame.origin.y / readerViewBounds.size.height;
            y = scanViewFrame.origin.x / readerViewBounds.size.width;
            width = scanViewFrame.size.height / readerViewBounds.size.height;
            height = scanViewFrame.size.width / readerViewBounds.size.width;
    
            return CGRectMake(x, y, width, height);
        }
    
        // 获取扫描结果,AVCaptureMetadataOutputObjectsDelegate 协议方法
        - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
                                                                         fromConnection:(AVCaptureConnection *)connection {
    
            if (metadataObjects.count > 0) {
    
                // 停止扫描
                [self.session stopRunning];
    
                AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0];
    
                // 获取扫描结果
                NSString *resultString = metadataObject.stringValue;
    
                // 输出扫描字符串
                [[[UIAlertView alloc] initWithTitle:@"扫描成功"
                                            message:resultString
                                           delegate:nil
                                  cancelButtonTitle:@"确定"
                                  otherButtonTitles:nil] show];
            }
        }
    
  • 部分初始化的代码加上实今世理方法便形成了二维码扫描的做事,这里我们必要注意的是,在二维码扫描的时候,大家通常都会在显示屏中间放一个四方,用来展现二维码扫描的深浅区间,这里大家在AVCaptureMetadataOutput 类中有二个 rectOfInterest 属性,它的效果与利益正是安装扫描范围。那些CGRect 参数和平凡的 Rect 范围不太相似,它的八个值的限制都以0-1,表示比例。rectOfInterest 都以依据横屏来计算的,所以当竖屏之处下 x 轴和 y 轴要换到一下,宽度和惊人设置的气象也是相似。

  • 效果

    图片 3 图片 4

接下去,大家表明多少个必要的习性

2.1 成立项目

打开Xcode,创造二个新的类型(FileNewProject...),选择iOS一栏下的Application中的Single View Application模版,然后点击Next,填写项目选项。在Product Name中填写QRCodeReaderDemo,选择Objective-C语言,点击Next,选择文件地方,并单击Create成立项目。

2.2 读取二维码

  • 读取首要用到 CoreImage 然则要强调的是读取二维码的功能唯有在 iOS8 之后才支撑,大家须要在相册中调用一个二维码,将其读取,代码如下

        // 遵守协议
        <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
    
        // 打开相册,选取图片,自定义方法
        - (void)readQRCode {
    
            if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
    
                // 初始化相册拾取器
                UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    
                // 设置资源
                picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    
                // 设置代理
                picker.delegate = self;
    
                [self presentViewController:picker animated:YES completion:nil];
    
            } else {
    
                NSString *errorStr = [NSString stringWithFormat:@"请在系统设置->隐私->照片中允许 "%@" 使用照片。",
                                      [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]];
    
                [[[UIAlertView alloc] initWithTitle:@"读取失败"
                                            message:errorStr
                                           delegate:nil
                                  cancelButtonTitle:@"确定"
                                  otherButtonTitles:nil] show];
            }
        }
    
        // 获取选中的图片,imagePickerController 协议方法
        - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    
            // 获取选择的图片
            UIImage *image = info[UIImagePickerControllerOriginalImage];
    
            // 识别图片中的二维码
            NSString *resultString = [self recognizeQRCodeFromImage:image];
    
            // 输出扫描字符串
            [[[UIAlertView alloc] initWithTitle:@"读取成功"
                                        message:resultString
                                       delegate:nil
                              cancelButtonTitle:@"确定"
                              otherButtonTitles:nil] show];
    
            // 返回
            [picker dismissViewControllerAnimated:YES completion:nil];
        }
    
        // 识别图片中的二维码
        - (NSString *)recognizeQRCodeFromImage:(UIImage *)image {
    
            CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];
    
            // 初始化扫描仪,设置识别类型和识别质量
            CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode
                                                      context:nil
                                                      options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    
            // 扫描获取的特征组
            NSArray *features = [detector featuresInImage:ciImage];
    
            if (features.count >= 1) {
    
                // 获取扫描结果
                CIQRCodeFeature *feature = [features objectAtIndex:0];
                NSString *resultString = feature.messageString;
    
                return resultString;
    
            } else {
    
                return @"该图片不包含二维码";
            }
        }
    
  • 效果

    图片 5 图片 6

@interface QRCodeScanViewController () <AVCaptureMetadataOutputObjectsDelegate>//采集的设备@property (strong,nonatomic) AVCaptureDevice * device;//设备的输入@property (strong,nonatomic) AVCaptureDeviceInput * input;//输出@property (strong,nonatomic) AVCaptureMetadataOutput * output;//采集流@property (strong,nonatomic) AVCaptureSession * session;//窗口@property (strong,nonatomic) AVCaptureVideoPreviewLayer * previewLayer;//扫描框@property (nonatomic, strong) UIView * view_bg;//扫描线@property (nonatomic, strong) CALayer * layer_scanLine;//提示语@property (nonatomic, strong) UILabel * lab_word;//扫描线定时器@property (nonatomic, strong) NSTimer * timer;@end

2.2 创设分界面

打开Main.storyboard文本,在近来调整器中放到导航调整器,并增添标题QR Code Reader

在视图调节器中增添ToolBar、Flexible Space Bar Button Item、Bar Button Item、View,布局如下:

中间,各因素及功用:

  • ToolBar:增多在调控器视图的最尾部,其Bar Item标题为Start,具备双重功效,用于运转和休息扫描。
  • Flexible Space Bar Button Item:分别加多在Start的左右两边,用于固定Start 之处使其居中彰显。
  • Bar Button Item:增多在导航栏的右侧,标题为Album,用于从相册接受二维码图片打开深入解析。
  • View:增加在调节器视图的中档,用于稍后设置扫描框。在这里地运用自动构造一定宽高均为260,何况水平和垂直方向都以居中。

开创一个名称叫ScanView的新文件(FileNewFile…),它是UIView的子类。然后选中央电台图调控器中间加多的View,将该视图的类名更正为ScanView

张开扶持理编辑辑器,将storyboard中的成分连接到代码中:

注意,需要在ViewController.m文本中程导弹入ScanView.h文件。

2.3 长按识别二维码

  • 这一个功能有众多的地点在用,最令人驾驭的自小编想就是Wechat了,其实完毕情势照旧超级轻易的。

        // 创建图片,添加长按手势
        - (void)recognizeQRCode {
    
            CGFloat margin = 50;
            CGRect scanFrame = CGRectMake(margin,
                                          margin   20,
                                          self.view.bounds.size.width - margin * 2,
                                          self.view.bounds.size.width - margin * 2);
    
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:scanFrame];
            imageView.image = [UIImage imageNamed:@"demo"];
    
            imageView.userInteractionEnabled = YES;
    
            [self.view addSubview:imageView];
    
            UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self 
                                                                                                    action:@selector(dealLongPress:)];
            [imageView addGestureRecognizer:longPress];
        }
    
        // 处理长按手势,识别图片中的二维码
        - (void)dealLongPress:(UIGestureRecognizer *)gesture {
    
            if (gesture.state == UIGestureRecognizerStateBegan) {
    
                UIImageView *pressedImageView = (UIImageView *)gesture.view;
    
                if (pressedImageView.image) {
    
                    // 识别图片中的二维码
                    NSString *resultString = [self recognizeQRCodeFromImage:pressedImageView.image];
    
                    // 输出扫描字符串
                    [[[UIAlertView alloc] initWithTitle:@"识别成功"
                                                message:resultString
                                               delegate:nil
                                      cancelButtonTitle:@"确定"
                                      otherButtonTitles:nil] show];
    
                } else {
                    [[[UIAlertView alloc] initWithTitle:@"识别失败"
                                                message:@"该图片不包含二维码"
                                               delegate:nil
                                      cancelButtonTitle:@"确定"
                                      otherButtonTitles:nil] show];
                }
            }
        }
    
        // 识别图片中的二维码
        - (NSString *)recognizeQRCodeFromImage:(UIImage *)image {
    
            CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];
    
            // 初始化扫描仪,设置识别类型和识别质量
            CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode
                                                      context:nil
                                                      options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    
            // 扫描获取的特征组
            NSArray *features = [detector featuresInImage:ciImage];
    
            if (features.count >= 1) {
    
                // 获取扫描结果
                CIQRCodeFeature *feature = [features objectAtIndex:0];
                NSString *resultString = feature.messageString;
    
                return resultString;
    
            } else {
    
                return @"该图片不包含二维码";
            }
        }
    
  • 效果

    图片 7 图片 8

此间要留意的是内需遵从AVCaptureMetadataOutputObjectsDelegate这一个代理公约,扫描结果根本是在代理方法中赢得。

2.3 增多代码

2.4 生成二维码

  • 生成二维码,其实也是用到 CoreImage,然而步骤冗杂一些,代码如下

        // 创建 ImageView,存放生成的二维码
        - (void)createQRCode {
    
            CGFloat margin = 50;
            CGRect frame = CGRectMake(margin,
                                      margin   20,
                                      self.view.bounds.size.width - margin * 2,
                                      self.view.bounds.size.width - margin * 2);
    
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
            [self.view addSubview:imageView];
    
            // 生成二维码
            [self createQRCodeToImageView:imageView
                               fromString:@"qianchia"
                                 withIcon:[UIImage imageNamed:@"demo1"]
                                withColor:[UIColor redColor]];
        }
    
        // 生成二维码
        - (void)createQRCodeToImageView:(UIImageView *)imageView
                             fromString:(NSString *)inputString
                               withIcon:(UIImage *)icon
                              withColor:(UIColor *)color {
    
            // 创建过滤器
            CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    
            // 恢复默认
            [filter setDefaults];
    
            // 给过滤器添加数据
            NSData *data = [inputString dataUsingEncoding:NSUTF8StringEncoding];
    
            // 通过 KVO 设置滤镜 inputMessage 数据
            [filter setValue:data forKey:@"inputMessage"];
    
            // 设置二维码颜色
            UIColor *onColor = color ? : [UIColor blackColor];
            UIColor *offColor = [UIColor whiteColor];
    
            CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"
                                               keysAndValues:@"inputImage", filter.outputImage,
                                                             @"inputColor0", [CIColor colorWithCGColor:onColor.CGColor],
                                                             @"inputColor1", [CIColor colorWithCGColor:offColor.CGColor],
                                                             nil];
    
            // 获取输出的二维码
            CIImage *outputImage = colorFilter.outputImage;
            // CIImage *outputImage = [filter outputImage];
    
            CIContext *context = [CIContext contextWithOptions:nil];
            CGImageRef cgImage = [context createCGImage:outputImage
                                               fromRect:[outputImage extent]];
    
            // 将 CIImage 转换成 UIImage,并放大显示
            UIImage *qrImage = [UIImage imageWithCGImage:cgImage
                                                 scale:1.0
                                           orientation:UIImageOrientationUp];
    
            // 重绘 UIImage,默认情况下生成的图片比较模糊
            CGFloat scale = 100;
            CGFloat width = qrImage.size.width * scale;
            CGFloat height = qrImage.size.height * scale;
    
            UIGraphicsBeginImageContext(CGSizeMake(width, height));
            CGContextRef context1 = UIGraphicsGetCurrentContext();
            CGContextSetInterpolationQuality(context1, kCGInterpolationNone);
            [qrImage drawInRect:CGRectMake(0, 0, width, height)];
            qrImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            CGImageRelease(cgImage);
    
            // 添加头像
    
            if (icon) {
                UIGraphicsBeginImageContext(qrImage.size);
                [qrImage drawInRect:CGRectMake(0, 0, qrImage.size.width, qrImage.size.height)];
    
                // 设置头像大小
                CGFloat scale = 5;
                CGFloat width = qrImage.size.width / scale;
                CGFloat height = qrImage.size.height / scale;
                CGFloat x = (qrImage.size.width - width) / 2;
                CGFloat y = (qrImage.size.height - height) / 2;
                [icon drawInRect:CGRectMake( x,  y, width, height)];
    
                UIImage *newImage =  UIGraphicsGetImageFromCurrentImageContext();
                UIGraphicsEndImageContext();
    
                imageView.image = newImage;
    
            } else {
                imageView.image = qrImage;
            }
        }
    
  • 效果

    图片 9 图片 10

本文由新浦京81707con发布于功能介绍,转载请注明出处:使用AVFoundation读取二维码,原生二维码扫描

关键词: 新浦京81707con ios 二维码 iOS-APP iOS 10

上一篇:iOS开发小冷易忘知识点总结,iOS中UITableView性能优

下一篇:没有了