公司最近要添加一个二维码扫描网页登录的功能,所以最近研究了一下iOS关于二维码扫描的功能
因为移动端不涉及二维码的生成,所以只做关于二维码的扫描(这里感谢一下SGQRCode的作者)
摄像头扫描二维码
用摄像头扫描二维码就需要我们刚才说的AVCaptureSession,具体怎么实现,我们来看demo。
框架
首先导入我们要用到的框架,因为扫描和识别都是在系统AVFoundation下的做的,所以第一步就要把框架主头文件导入 #import <AVFoundation/AVFoundation.h>
头文件
/** 回话对象属性*/
@property (nonatomic, strong) AVCaptureSession *session;
/** 摄像头预览图层*/
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
实现部分
// 创建回话对象
self.session = [[AVCaptureSession alloc] init];
// 设置回话对象图像采集率
[self.session setSessionPreset:AVCaptureSessionPresetHigh];
// 获取摄像头设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 创建回话输入对象
NSError *inputError = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&inputError];
// 添加会话输入
[self.session addInput:input];
// 创建输出对象
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
// 设置输出对象代理
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 添加会话输出
[self.session addOutput:output];
// 设置输出数据类型,需要将元数据输出添加到回话后,才能制定元数据,否则会报错
// 二维码和条形码可以一起设置
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
// 创建摄像头预览图层
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer.frame = self.view.frame;
// 将图层插入当前图层
[self.view.layer insertSublayer:self.previewLayer atIndex:0];
// 启动会话
[self.session startRunning];
主体方法实现了以后,我们来看一下AVCaptureMetadataOutputObjectsDelegate方法- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection;,当二维码扫描成功之后,系统将自动调用这个代理方法。
AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
// 1、如果扫描完成,停止会话
[self.session stopRunning];
// 2、删除预览图层
[self.previewLayer removeFromSuperlayer];
// 3、设置界面显示扫描结果
if (metadataObjects && metadataObjects.count > 0) {
// 播放音效
NSString *audioFile = [[NSBundle mainBundle] pathForResource:@"sound" ofType:@"wav"];
NSURL *fileUrl = [NSURL fileURLWithPath:audioFile];
SystemSoundID soundID = 0;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
AudioServicesPlaySystemSound(soundID); // 播放音效
AVMetadataMachineReadableCodeObject *obj = metadataObjects.firstObject;
// 取出二维码扫描到的内容
NSLog(@"二维码内容:::%@",obj.stringValue);
}
}
// 播放音效成功回调
void soundCompleteCallback (){
}
如果想监听设备摄像头实时的视频流就将会话输出对象添加AVCaptureVideoDataOutputSampleBufferDelegate代理监听实现- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;代理方法
AVCaptureVideoDataOutputSampleBufferDelegate
[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
实现方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// 这个方法会时时调用,但内存很稳定
CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
CFRelease(metadataDict);
NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
float brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
NSLog(@"%f",brightnessValue);
}