前段时间,总结了一下如何生成二维码,如果想了解可以移步二维码的生成。下面我们进入正题。
再说扫描二维码之前,我们先来说说AVCaptureSession
。AVCaptureSession
是AVFoundation
框架的核心类,用于捕捉视频和音频,协调视频和音频的输入和输出流.下面是围绕AVCaptureSession
的图
下面我们来正式说一下,如何实现二维码扫描功能。
一、用摄像头扫描
用摄像头扫描二维码就需要我们刚才说的AVCaptureSession
,具体怎么实现,我们来看demo
。
首先声明两个类:
/// 会话对象
@property (nonatomic, strong) AVCaptureSession *session;
/// 图层类
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
下面来看具体方法的实现:
- (void)createQRCodeScanning {
// 1、获取摄像设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2、创建输入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
// 3、创建输出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
// 4、设置代理 在主线程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 设置扫描范围(每一个取值0~1,以屏幕右上角为坐标原点)
// 注:微信二维码的扫描范围是整个屏幕,这里并没有做处理(可不用设置)
output.rectOfInterest = CGRectMake(0.05, 0.2, 0.7, 0.6);
// 5、初始化链接对象(会话对象)
self.session = [[AVCaptureSession alloc] init];
// 高质量采集率
[_session setSessionPreset:AVCaptureSessionPresetHigh];
// 5.1 添加会话输入
[_session addInput:input];
// 5.2 添加会话输出
[_session addOutput:output];
// 6、设置输出数据类型,需要将元数据输出添加到会话后,才能指定元数据类型,否则会报错
// 设置扫码支持的编码格式(如下设置条形码和二维码兼容)
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
// 7、实例化预览图层, 传递_session是为了告诉图层将来显示什么内容
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
_previewLayer.frame = self.view.layer.bounds;
// 8、将图层插入当前视图
[self.view.layer insertSublayer:_previewLayer atIndex:0];
// 9、启动会话
[_session startRunning];
}
主体方法实现了以后,我们来看一下AVCaptureMetadataOutputObjectsDelegate
方法- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection;
,当二维码扫描成功之后,系统将自动调用这个代理方法。
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
// 1、如果扫描完成,停止会话
[self.session stopRunning];
// 2、删除预览图层
[self.previewLayer removeFromSuperlayer];
// 3、设置界面显示扫描结果
if (metadataObjects.count > 0) {
AVMetadataMachineReadableCodeObject *obj = metadataObjects[0];
NSLog(@"二维码内容:::%@",obj.stringValue);
}
}
二、扫描相册中的二维码
以上是用摄像头扫描二维码功能的实现,而类似微信扫一扫等大部分的扫码功能都支持扫描相册中的二维码图片,下面我们来看一下如何实现扫描相册中的二维码。
要想扫描相册中的二维码,首先我们需要先将图片从相册中取出来,实现如下:
- (void)readImageFromAlbum {
// 1、 获取摄像设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (device) {
// 判断授权状态
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusNotDetermined) { // 用户还没有做出选择
// 弹框请求用户授权
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) { // 用户第一次同意了访问相册权限
dispatch_async(dispatch_get_main_queue(), ^{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //(选择类型)表示仅仅从相册中选取照片
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:nil];
});
} else { // 用户第一次拒绝了访问相机权限
}
}];
} else if (status == PHAuthorizationStatusAuthorized) { // 用户允许当前应用访问相册
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //(选择类型)表示仅仅从相册中选取照片
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:nil];
} else if (status == PHAuthorizationStatusDenied) { // 用户拒绝当前应用访问相册
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"⚠️ 警告" message:@"请去-> [设置 - 隐私 - 照片 - SGQRCodeExample] 打开访问开关" preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
}];
[alertC addAction:alertA];
[self presentViewController:alertC animated:YES completion:nil];
} else if (status == PHAuthorizationStatusRestricted) {
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"由于系统原因, 无法访问相册" preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
}];
[alertC addAction:alertA];
[self presentViewController:alertC animated:YES completion:nil];
}
}
}
下面我们来实现UIImagePickerControllerDelegate
代理方法,这个代理方法会在从相册中获取图片成功后调用
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
//获取选中的图片
UIImage *iamge = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
[self dismissViewControllerAnimated:YES completion:^{
//识别图片中二维码的方法
[self scanQRCodeFromPhotosInTheAlbum: iamge];
}];
}
获取到图片后就可以分析图片中的二维码,具体代码:
- (void)scanQRCodeFromPhotosInTheAlbum:(UIImage *)image {
// CIDetector(CIDetector可用于人脸识别)进行图片解析,从而使我们可以便捷的从相册中获取到二维码
// 声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
// 取得识别结果
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
for (int index = 0; index < [features count]; index ++) {
CIQRCodeFeature *feature = [features objectAtIndex:index];
NSString *scannedResult = feature.messageString;
NSLog(@"scannedResult - - %@", scannedResult);
}
}
以上是利用iOS
原生方法实现的二维码的扫描方法,不足之处,还望多多指正。
注:以上方法是根据他人上传GitHub的开源项目:SGQRCode学习总结的,有兴趣或是有需要的可以下载学习。