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

  最近项目上需要开发扫描二维码进行签到的功能,主要用于开会签到的场景,所以为了避免作弊,我们再开发时只采用直接扫描的方式,并且要屏蔽从相册读取图片,此外还在二维码扫描成功签到时后台会自动上传用户的当前地点,如何自动定位获取用户的当前地点在上一篇随笔iOS学习——自动定位中已经讲过了,本文就简单地说一下如何利用iOS原生的模块实现二维码的扫描。

  二维码扫描是很多应用都会实现的功能,比较著名的第三方开源库是Google出品的ZXing,其的OC的移植版本是ZXingObjc。iOS系统原生的二维码扫描模块是在iOS7之后推出的,它主要是利用iOS设备的后置摄像头进行实现的。

要调用系统的摄像头识别二维码,我们需要导入系统的AVFoundation库。使用系统的摄像头,我们一般的需要以下五个对象:一个后置摄像头设备(AVCaptureDevice)、一个输入(AVCaptureDeviceInput)、一个输出(AVCaptureMetadataOutput)、一个协调控制器(AVCaptureSession)、一个预览层(AVCaptureVideoPreviewLayer),此外为了更好的体验效果,我们加入了缩放手势,在进行二维码扫描的时候可以手动进行缩放扫描区域,以获得更好的扫描效果。

@interface CJScanQRCodeViewController () <AVCaptureMetadataOutputObjectsDelegate>

@property (strong, nonatomic) AVCaptureDevice * device; //捕获设备,默认后置摄像头
@property (strong, nonatomic) AVCaptureDeviceInput * input; //输入设备
@property (strong, nonatomic) AVCaptureMetadataOutput * output;//输出设备,需要指定他的输出类型及扫描范围
@property (strong, nonatomic) AVCaptureSession * session; //AVFoundation框架捕获类的中心枢纽,协调输入输出设备以获得数据
@property (strong, nonatomic) AVCaptureVideoPreviewLayer * previewLayer;//展示捕获图像的图层,是CALayer的子类

@property (strong, nonatomic) UIPinchGestureRecognizer *pinchGes;//缩放手势
@property (assign, nonatomic) CGFloat scanRegion_W;//二维码正方形扫描区域的宽度,根据不同机型适配

@end

  首先,我们是需要进行对我们的一些设备进行配置,比喻需要用到自动定位,就需要对定位信息进行配置,接着对二维码扫描的相关设备进行配置,然后对我们的缩放手势进行设置,都配置完之后,直接开始启动二维码扫描就可以了,成功扫码并识别到信息时候会调用对应的 AVCaptureMetadataOutputObjectsDelegate 代理的 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection 方法进行后期处理,我们需要实现代理的该方法,在其中编写我们需要的功能逻辑。

- (void)viewDidLoad {
    [super viewDidLoad];
    //页面标题
    self.title = @"扫一扫";
    //配置定位信息
    [self configLocation];
    //配置二维码扫描
    [self configBasicDevice];
    //配置缩放手势
    [self configPinchGes];
    //开始启动
    [self.session startRunning];
}

  关于二维码扫描设备的配置流程,一般地,我们先将需要的五大设备进行初始化,然后需要进行对应的设置没具体的设置流程和方法见下面的代码和注释。

- (void)configBasicDevice{
    //默认使用后置摄像头进行扫描,使用AVMediaTypeVideo表示视频
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    //设备输入 初始化
    self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
    //设备输出 初始化,并设置代理和回调,当设备扫描到数据时通过该代理输出队列,一般输出队列都设置为主队列,也是设置了回调方法执行所在的队列环境
    self.output = [[AVCaptureMetadataOutput alloc]init];
    [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    //会话 初始化,通过 会话 连接设备的 输入 输出,并设置采样质量为 高
    self.session = [[AVCaptureSession alloc]init];
    [self.session setSessionPreset:AVCaptureSessionPresetHigh];
    //会话添加设备的 输入 输出,建立连接
    if ([self.session canAddInput:self.input]) {
        [self.session addInput:self.input];
    }
    if ([self.session canAddOutput:self.output]) {
        [self.session addOutput:self.output];
    }
    //指定设备的识别类型 这里只指定二维码识别这一种类型 AVMetadataObjectTypeQRCode
    //指定识别类型这一步一定要在输出添加到会话之后,否则设备的课识别类型会为空,程序会出现崩溃
    [self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
    //设置扫描信息的识别区域,本文设置正中央的一块正方形区域,该区域宽度是scanRegion_W
    //这里考虑了导航栏的高度,所以计算有点麻烦,识别区域越小识别效率越高,所以不设置整个屏幕
    CGFloat navH = self.navigationController.navigationBar.bounds.size.height;
    CGFloat viewH = ZYAppHeight - navH;
    CGFloat scanViewH = self.scanRegion_W;
    [self.output setRectOfInterest:CGRectMake((ZYAppWidth-scanViewH)/(2*ZYAppWidth), (viewH-scanViewH)/(2*viewH), scanViewH/ZYAppWidth, scanViewH/viewH)];
    //预览层 初始化,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示
    //预览层的区域设置为整个屏幕,这样可以方便我们进行移动二维码到扫描区域,在上面我们已经对我们的扫描区域进行了相应的设置
    self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
    self.previewLayer.frame = CGRectMake(0, 0, ZYAppWidth, ZYAppHeight);
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.view.layer addSublayer:self.previewLayer];
    //扫描框 和扫描线的布局和设置,模拟正在扫描的过程,这一块加不加不影响我们的效果,只是起一个直观的作用
    TNWCameraScanView *clearView = [[TNWCameraScanView alloc]initWithFrame:self.view.frame navH:navH];
    [self.view addSubview:clearView];
    //扫描框下面的信息label布局
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, (viewH+scanViewH)/2+10.0f, ZYAppWidth, 20.0f)];
    label.text = @"扫一扫功能仅用于会议签到";
    label.font = FONT(15.0f);
    label.textColor = [UIColor whiteColor];
    label.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:label];
}

  接下来我们看一下如何配置我们的缩放手势,这个相对而言就很简单了,我们直接在self.view上添加一个缩放手势,并在对应的方法中对我们的相机设备的焦距进行修改就达到了缩放的目的。

- (void)configPinchGes{
    self.pinchGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchDetected:)];
    [self.view addGestureRecognizer:self.pinchGes];
}

- (void)pinchDetected:(UIPinchGestureRecognizer*)recogniser{
    if (!_device){
        return;
    }
   //对手势的状态进行判断
    if (recogniser.state == UIGestureRecognizerStateBegan){
        _initScale = _device.videoZoomFactor;
    }
    //相机设备在改变某些参数前必须先锁定,直到改变结束才能解锁
    NSError *error = nil;
    [_device lockForConfiguration:&error]; //锁定相机设备
    if (!error) {
        CGFloat zoomFactor; //缩放因子
        CGFloat scale = recogniser.scale;
        if (scale < 1.0f) {
            zoomFactor = self.initScale - pow(self.device.activeFormat.videoMaxZoomFactor, 1.0f - recogniser.scale);
        } else {
            zoomFactor = self.initScale + pow(self.device.activeFormat.videoMaxZoomFactor, (recogniser.scale - 1.0f) / 2.0f);
        }
        zoomFactor = MIN(15.0f, zoomFactor);
        zoomFactor = MAX(1.0f, zoomFactor);
        _device.videoZoomFactor = zoomFactor;
        [_device unlockForConfiguration];
    }
} 

最后,我们需要重写代理的回调方法,实现我们在成功识别二维码之后要实现的功能逻辑。这样我们的二维码扫描功能就完成了。

#pragma mark - AVCaptureMetadataOutputObjectsDelegate
//后置摄像头扫描到二维码的信息
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    [self.session stopRunning];   //停止扫描
    if ([metadataObjects count] >= 1) {
        //数组中包含的都是AVMetadataMachineReadableCodeObject 类型的对象,该对象中包含解码后的数据
        AVMetadataMachineReadableCodeObject *qrObject = [metadataObjects lastObject];
        //拿到扫描内容在这里进行个性化处理
        NSString *result = qrObject.stringValue;
        //解析数据进行处理并实现相应的逻辑
        //代码省略
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏伪君子的梦呓

ScreenToGif --好用的gif录屏/剪辑软件

引言 遇到想向别人展示一下神操作,或者想向让别人看一下某些效果时,一张图片说不清,弄个视频又太麻烦,这可怎么好呢?其实一个ScreenToGif就可以解决了。 ...

3205
来自专栏移动开发之家

从Android到React Native开发(一、入门)

 大家好┏ (^ω^)=,许久不见,一不小心断更就成为了一种习惯,因为最近掉React Native的坑里,无法自拔啊~(╯‵□′)╯︵┻━┻。

1012
来自专栏Coco的专栏

《HTML重构》读书笔记&思维导图

1854
来自专栏程序员的诗和远方

React Native 实现二维码扫描

最近刚好在学习 React Native 想搞个扫描条形码,二维码的小应用,因为涉及硬件接口,而且自己本身并没有原生开发背景,踩了几个坑,记录一下。 扫描...

6368
来自专栏移动开发之家

从Android到React Native开发(一、入门)

大家好┏ (ω)=,许久不见,一不小心断更就成为了一种习惯,因为最近掉React Native的坑里,无法自拔啊~(╯‵□′)╯︵┻━┻。 关于React N...

942
来自专栏糊一笑

微信小程序初探【类微信UI聊天简单实现】

微信小程序最近很火,火到什么程度,只要你一打开微信,就是它的身影,几乎你用的各个APP都可以在微信中找到它的复制版,另外官方自带的跳一跳更是将它推到了空前至高的...

6045
来自专栏微信小开发

小程序界面设计指南

“上一期文章讲了小程序平台的特点以及场景需求,这一期文章主讲小程序设计规范,这是我通过阅读官方文档后归纳总结的,需要详细了解的小伙伴可以去看官方设计指南,文末有...

6937
来自专栏phodal

我的第四款编辑器:微信公众号上使用 Markdown 来显示代码

这已经是我第四次写编辑器了~~~ 第一次是在三年前(2014年4月份),当时我听说有一个工具叫 Node-Webkit,于是我就结合CodeMirror撸了一个...

2238
来自专栏有刻

Mac 小记 — 杂录

3716
来自专栏码农笔录

vue2(webpack)调用amap高德地图及其UI组件

3671

扫码关注云+社区

领取腾讯云代金券