新建一个项目,在storyboard中设置ViewController为首页,同时添加一个ScanCodeViewController作为二维码扫描页面。
在ViewController页面新增一个按钮,点击按钮触发scanCode方法:
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake((self.view.frame.size.width-150)/2, 200, 150, 50)];
[btn setTitleColor:[UIColor whiteColor] forState:0];
[btn setTitle:@"扫描二维码" forState:0];
[btn setBackgroundColor:[UIColor orangeColor]];
[btn addTarget:self action:@selector(scanCode:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
由于扫描二维码需要相机或者相册,所以需要再次判断是否授权。
若有访问相机、相册的权限则跳转,否则打印相应信息
- (void)scanCode:(id)sender{
//iOS 8 后,全部都要授权
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
//许可对话没有出现,发起授权许可
case AVAuthorizationStatusNotDetermined:{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {//第一次用户接受
ScanCodeViewController *scanVC = [[ScanCodeViewController alloc] init];
[self presentViewController:scanVC animated:YES completion:nil];
}else{//用户拒绝
NSLog(@"用户明确地拒绝授权,请打开权限");
}
}];
break;
}
//已经开启授权,可继续
case AVAuthorizationStatusAuthorized:{
ScanCodeViewController *scanVC = [[ScanCodeViewController alloc] init];
[self presentViewController:scanVC animated:YES completion:nil];
break;
}
//用户明确地拒绝授权,或者相机设备无法访问
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
NSLog(@"用户明确地拒绝授权,请打开权限");
break;
default:
break;
}
}
接下来就是进入ScanCodeViewController页面了,二维码扫描语音获取摄像头并读取照片信息,因此我们需要导入系统的AVFoundation框架,创建视频会话。在TARGETS->Build Phases->Link Binary With Libaries中点击“+”按钮,在弹出的窗口中找到AVFoundation.framework添加。
在ScanCodeViewController.h页面导入头文件及协议
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface ScanCodeViewController : UIViewController<AVCaptureMetadataOutputObjectsDelegate,
UINavigationBarDelegate,UIImagePickerControllerDelegate>
@property (nonatomic, strong) AVCaptureSession *session;
@property (nonatomic, weak) UIView *maskView;
@property (nonatomic, strong) UIView *scanWindow;
@property (nonatomic, strong) UIImageView *scanNetImageView;
@end
AVCaptureSession 会话对象,此类作为硬件设备输入输出信息的桥梁没承担试试获取设备数据的责任;
AVCaptureDeviceInput 设备输入类,这个类用来表示输入数据的硬件设备,配置抽象设备的port;
AVCaptureMetadataOutput 输出类,这个支持二维码、条形码等图像数据的识别;
AVCaptureVideoPreviewLayer 图层类,用来快速呈现摄像头获取的原始数据。
实现二维码扫描主要是以下步骤:
1.创建设备会话对象,用来设置设备数据输入;
2.获取摄像头,并且将摄像头对象加入当前会话中;
3.实时获取摄像头原始数据显示在屏幕上;
4.扫描到二维码数据,通过协议方法回调。
- (void)beginScanning {
//初始化会话对象
_session = [[AVCaptureSession alloc]init];
//高质量采集率
[_session setSessionPreset:AVCaptureSessionPresetHigh];
//获取摄像设备
AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if (!input) {
NSLog(@"没有摄像头");
return;
}
//创建输出流
AVCaptureMetadataOutput * output = [[AVCaptureMetadataOutput alloc]init];
//设置代理 在主线程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//设置有效扫描区域
CGRect scanCrop = CGRectMake(
(self.view.frame.size.height-_scanWindow.bounds.size.height)/2/self.view.frame.size.height,
(self.view.frame.size.width-_scanWindow.bounds.size.width)/2/self.view.frame.size.width,
_scanWindow.bounds.size.height/self.view.frame.size.height,
_scanWindow.bounds.size.width/self.view.frame.size.width);
output.rectOfInterest = scanCrop;
//设置扫码支持的编码格式(如下设置条形码和二维码兼容)
output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
[_session addInput:input];
[_session addOutput:output];
AVCaptureVideoPreviewLayer * layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
layer.videoGravity=AVLayerVideoGravityResizeAspectFill;
layer.frame=self.view.layer.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
//开始捕获
[_session startRunning];
}
扫描之后,还要实现AVCaptureMetadataOutputObjectsDelegate协议中的captureOutput:didOutputMetadataObjects:fromConnection:方法,来获取扫描得到的数据。
//此方法是在识别到QRCode并且完成转换,如果QRCode的内容越大,转换需要的时间就越长。
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
[_session stopRunning];
//参数metadataObjects数组中存放扫描结果
if ([metadataObjects count] > 0) {
/**结果对象 */
CIQRCodeFeature *feature = [metadataObjects objectAtIndex:0];
NSString *scannedResult = feature.messageString;
NSURL * url = [NSURL URLWithString: scannedResult];
if ([[UIApplication sharedApplication] canOpenURL: url]) {
[[UIApplication sharedApplication] openURL: url];
[_session startRunning];
} else {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"扫描结果" message:scannedResult preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *back = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[_session startRunning];
}];
[controller addAction:back];
[self presentViewController:controller animated:YES completion:nil];
}
} else {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"扫描结果" message:@"该图片没有包含一个二维码!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *back = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[_session startRunning];
}];
[controller addAction:back];
[self presentViewController:controller animated:YES completion:nil];
}
}