主要涉及的类:
AVCaptureDevice代表了物理捕获设备如:摄像机。用于配置等底层硬件设置相机的自动对焦模式。
AVCaptureDeviceInput是AVCaptureInput的子类,可以作为输入捕获会话,用AVCaptureDevice实例初始化。
AVCaptureSession管理输入(AVCaptureInput)和输出(AVCaptureOutput)流,包含开启和停止会话方法。
AVCaptureMetadataOutput是AVCaptureOutput的子类,处理输出捕获会话。捕获的对象传递给一个委托实线AVCaptureMetadataOutputObjectsDelegate协议。协议方法在指定的派发队列(dispatch queue)上执行。
AVCaptureVideoPreviewLayer是CALayer的一个子类,显示捕获到的相机输出流。
实现的基本流程如下:
1.首先获取流媒体信息我们需要用到AVCaptureSession对象来管理输入流和输出流;
2.然后利用AVCaptureVideoPreviewLayer对象来显示信息。
下面开始上代码了...
1.导入AVFoundation Framework的头文件,并添加AVCaptureSession和AVCaptureVideoPreviewLayer的属性
#import "QRCodeScanViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface QRCodeScanViewController () <AVCaptureMetadataOutputObjectsDelegate>
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
// 闪关灯开启关闭标志
@property (nonatomic, assign) BOOL isLightOn;
@end
2.创建会话,读取输入流
// 开始扫描
- (BOOL)startScan {
// 获取手机硬件设备
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 初始化输入流
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
if (!input) {
NSLog(@"%@",[error localizedDescription]);
return NO;
}
// 创建会话
_captureSession = [[AVCaptureSession alloc] init];
// 添加输入流
[_captureSession addInput:input];
// 初始化输出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
// 添加输出流
[_captureSession addOutput:output];
// 创建dispatch queue
dispatch_queue_t queue = dispatch_queue_create(kQRCodeScanQueueName, DISPATCH_QUEUE_CONCURRENT);
//扫描的结果苹果是通过代理的方式区回调,所以outPut需要添加代理,并且因为扫描是耗时的工作,所以把它放到子线程里面
[output setMetadataObjectsDelegate:self queue:queue];
// 设置支持二维码和条形码扫描
[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
// 创建输出对象
_previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
_previewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:_previewLayer atIndex:0];
// 开始会话
[_captureSession startRunning];
return YES;
}
3.结束扫描
// 结束扫描
- (void)stopScan {
// 停止会话
[_captureSession stopRunning];
_captureSession = nil;
}
4.获取扫描结果并处理
#pragma mark -- AVCaptureMetadataOutputObjectsDelegate
// 扫描结果的代理回调方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
if (metadataObjects != nil && metadataObjects.count > 0) {
// 扫描到之后,停止扫描
[self stopScan];
// 获取结果并对其进行处理
AVMetadataMachineReadableCodeObject *object = metadataObjects.firstObject;
if ([[object type] isEqualToString:AVMetadataObjectTypeQRCode]) {
NSString *result = object.stringValue;
// 处理result
NSLog(@"%@",result);
} else {
NSLog(@"不是二维码");
}
}
}
5.通常在扫描的时候会有开启闪光灯的功能,也是通过AVCaptureDevice来实现的。添加isLightOn属性,设置初始值为NO,并添加开关闪光灯的按钮
// 开启和关闭闪光灯
- (IBAction)didClickedLightUpButton:(UIButton *)sender {
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//判断手机是否有闪光灯
if ([device hasTorch]) {
//呼叫手机操作系统,控制手机硬件
NSError *error = nil;
[device lockForConfiguration:&error];
if (self.isLightOn == NO) {
[device setTorchMode:AVCaptureTorchModeOn];
self.isLightOn = YES;
} else {
[device setTorchMode:AVCaptureTorchModeOff];
self.isLightOn = NO;
}
//结束对硬件的控制,跟上面的lockForConfiguration是配对的API
[device unlockForConfiguration];
}
}
最后附上github地址:https://github.com/chocklee/QRCodeScan