扫描大概流程:
- 创建设备对象,输入输出对象(这个比较好理解,扫描毕竟需要手机设备,扫描肯定是有输入输出操作)
- 创建session来管理输入输出对象,设置扫描要支持的格式比如二维码条形码
- 设置扫描窗口,这个就是你要设置扫描作用域范围
#import "ScanViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
@interface ScanViewController ()<AVCaptureMetadataOutputObjectsDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>{
AVCaptureSession *session;//输入输出的管理者
AVCaptureDevice *device;//获取摄像设备
AVCaptureDeviceInput *input;//创建输入流
AVCaptureMetadataOutput *output;//创建输出流
AVCaptureVideoPreviewLayer *layer;//扫描窗口
CGFloat imageX;
CGFloat imageY;
}
@property (nonatomic, strong) UIImagePickerController *imagePickerVC;
@property (nonatomic, strong) UIImageView *activeImage;
@property (nonatomic, strong) UIButton *torchButton;
@end
@implementation ScanViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"扫描";
self.view.backgroundColor = [UIColor whiteColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"相册" style:UIBarButtonItemStylePlain target:self action:@selector(libraryPhoto)];
imageX = DeviceSize.width*0.15;
imageY = DeviceSize.width*0.15+64;
// 判断相机权限
[self checkCaptureStatus];
[self initMaskView];
[self initScan];
[self performSelectorOnMainThread:@selector(timerFired) withObject:nil waitUntilDone:NO];
}
- (void)initMaskView{
//扫描的框框
UIImageView *scanImgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"card_box2"]];
scanImgView.frame = CGRectMake(imageX, imageY, DeviceSize.width*0.7, DeviceSize.width*0.7);
[self.view addSubview:scanImgView];
//扫描的线
UIImageView *lineImgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"card_thread"]];
lineImgView.frame = CGRectMake(imageX, imageY, DeviceSize.width*0.7, 8);
[self.view addSubview:lineImgView];
self.activeImage = lineImgView;
//扫描下面的提示按钮
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(scanImgView.frame), DeviceSize.width, 40)];
label.text = @"将二维码放入框内即可自动扫描";
label.textColor = [UIColor whiteColor];
label.font = [UIFont systemFontOfSize:15];
label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:label];
//添加全屏的黑色半透明蒙版
UIView *maskView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, DeviceSize.width, DeviceSize.height+20)];
maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
[self.view addSubview:maskView];
//从蒙版中扣出扫描框那一块,这块的大小尺寸将来也设成扫描输出的作用域大小
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:self.view.bounds];
[maskPath appendPath:[[UIBezierPath bezierPathWithRect:CGRectMake(imageX, imageY, DeviceSize.width*0.7, DeviceSize.width*0.7)]bezierPathByReversingPath]];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
maskLayer.path = maskPath.CGPath;
maskView.layer.mask = maskLayer;
}
- (void)initScan{
//获取摄像设备
device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//创建输出流
output = [[AVCaptureMetadataOutput alloc]init];
//设置代理 在主线程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//初始化session
session = [[AVCaptureSession alloc]init];
//高质量采集率
[session setSessionPreset:AVCaptureSessionPresetHigh];
[session addInput:input];
[session addOutput:output];
//设置扫码支持的格式(二维码和条形码)
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code];
layer = [AVCaptureVideoPreviewLayer layerWithSession:session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
//设置相机可是范围--全屏
layer.frame = self.view.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
//开始捕获
[session startRunning];
//设置扫描作用域范围(中间透明的扫描框)
CGRect intertRect = [layer metadataOutputRectOfInterestForRect:CGRectMake(imageX, imageY, DeviceSize.width*0.7, DeviceSize.width*0.7)];
output.rectOfInterest = intertRect;
}
//扫描线运动
- (void)timerFired{
[self.activeImage.layer addAnimation:[self time:2.5 Y:[NSNumber numberWithFloat:(DeviceSize.width*0.7-8)]] forKey:nil];
}
//动画 time单次滑动完成时间 y滑动距离
- (CABasicAnimation *)time:(float)time Y:(NSNumber *)y{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
animation.toValue = y;
animation.duration = time;
animation.removedOnCompletion = YES;//yes返回原位置
animation.repeatCount = MAXFLOAT;
animation.fillMode = kCAFillModeForwards;
return animation;
}
//代理方法扫描到的结果
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if (metadataObjects.count>0) {
[session stopRunning];
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0];
NSLog(@"stringValue = %@",metadataObject.stringValue);
}
}
- (void)checkCaptureStatus{
// 判断是否支持相机
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)) {
// 无权限
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法使用相机" message:@"请在iPhone的""设置-隐私-相机""中允许访问相机" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
[alert show];
}
}
- (void)libraryPhoto{
// 判断是否支持照片
PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus];
if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)) {
// 无权限
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法使用相册" message:@"请在iPhone的""设置""中允许访问照片" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
[alert show];
} else {
self.imagePickerVC = [[UIImagePickerController alloc]init];
self.imagePickerVC.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
self.imagePickerVC.mediaTypes = @[(NSString *)kUTTypeImage];
self.imagePickerVC.delegate = self;
self.imagePickerVC.allowsEditing = NO;
self.imagePickerVC.navigationBar.barTintColor = [UIColor whiteColor];
self.imagePickerVC.navigationBar.tintColor = kWord25Color;
[self.imagePickerVC.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : kWord25Color}];
[self presentViewController:_imagePickerVC animated:YES completion:NULL];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) { // 去设置界面,开启相机访问权限
if (ios8AndUper) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
} else {
}
}
}
// 选择图片成功调用此方法
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
UIImage *pickerImage = info[UIImagePickerControllerOriginalImage];
CIImage *ciImage = [CIImage imageWithData:UIImagePNGRepresentation(pickerImage)];
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy:CIDetectorAccuracyLow}];
NSArray *features = [detector featuresInImage:ciImage];
if (features.count >0) {
CIQRCodeFeature *feature = features[0];
NSLog(@"%@",feature.messageString);
}
[picker dismissViewControllerAnimated:NO completion:^{
}];
}
// 取消图片选择调用此方法
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
// dismiss UIImagePickerController
[self dismissViewControllerAnimated:YES completion:nil];
}
//开灯按钮事件处理
- (void)torchHandle{
[self setTorch:![self torchIsOn]];
}
// 开关手电筒 status yes-开,no-关
- (void)setTorch:(BOOL)status{
if (TARGET_IPHONE_SIMULATOR){
return;
}
Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
if (captureDeviceClass != nil) {
AVCaptureDevice *device = [captureDeviceClass defaultDeviceWithMediaType:AVMediaTypeVideo];
[device lockForConfiguration:nil];
if ( [device hasTorch] ){
if (status) {
[device setTorchMode:AVCaptureTorchModeOn];
[_torchButton setTitle:ZZYLocal(@"关灯") forState:UIControlStateNormal];
}else{
[device setTorchMode:AVCaptureTorchModeOff];
[_torchButton setTitle:ZZYLocal(@"开灯") forState:UIControlStateNormal];
}
}
[device unlockForConfiguration];
}
}
//判断设备灯光开关
- (BOOL)torchIsOn{
if (TARGET_IPHONE_SIMULATOR) {
return NO;
}
Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
if (captureDeviceClass != nil)
{
AVCaptureDevice *device = [captureDeviceClass defaultDeviceWithMediaType:AVMediaTypeVideo];
if ( [device hasTorch] ){
return [device torchMode] == AVCaptureTorchModeOn;
}
[device unlockForConfiguration];
}
return NO;
}
@end
借鉴:https://www.jianshu.com/p/09742ec7ec78
网友评论