自用-二维码扫描模块代码
效果图
直接上代码 注释写的还算详细
-需要准备下面的东西,其中preView是要显示的样式(就是摄像头画面加一个扫描框,因为扫描是不需要画面的,为了用户体验)
@interface ScanViewController () <AVCaptureMetadataOutputObjectsDelegate>
//1.输入设备(从外界采集信息) 输入设备很多种 摄像头 麦克风 键盘
@property (nonatomic, strong) AVCaptureDeviceInput* input;
//2.输出设备 (解析采集来得内容 然后获取到数据) Metadata 元数据
@property (nonatomic, strong) AVCaptureMetadataOutput* output;
//3.会话 session (连接输入和输出进行工作)
@property (nonatomic, strong) AVCaptureSession* session;//4.展示layer@property (nonatomic, strong) PreView* preView;
@end
-
接下来是扫描代码
#pragma mark--开始扫描
- (void)startScan
{//1.输入设备(从外界采集信息) //创建具体的设备 摄像头 //AVMediaTypeVideo 摄像头 AVMediaTypeAudio 麦克风 AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; self.input = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL]; //2.输出设备 (解析采集来得内容 然后获取到数据) Metadata 元数据 self.output = [[AVCaptureMetadataOutput alloc] init]; //3.会话 session (连接输入和输出进行工作) self.session = [[AVCaptureSession alloc] init]; //会话扫描展示的大小 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; //添加输入设备和输出设备 if ([self.session canAddInput:self.input]) { [self.session addInput:self.input]; } if ([self.session canAddOutput:self.output]) { [self.session addOutput:self.output]; } //指定输出设备的 代理 来返回 解析到得数据 [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; //设置元数据类型 QRCode 二维码+条形码 [self.output setMetadataObjectTypes:@[ AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code ]]; //.创建特殊视图展示二维码界面 self.preView = [[PreView alloc] initWithFrame:self.view.bounds]; self.preView.session = self.session; [self.view addSubview:self.preView]; //设置扫描区域(我设置的中间靠上w:300 h:300,这样不会打开就扫描上,用户体验会好点)-需要先设置frame 然后转化添加到上去 CGRect rect = CGRectMake(30, 100, 300, 300); CGRect interRect = [self.preView.previewLayer metadataOutputRectOfInterestForRect:rect]; self.output.rectOfInterest = interRect; //5.开启 会话 [self.session startRunning]; }
然后就是扫描到结果 从代理<AVCaptureMetadataOutputObjectsDelegate>的实现中拿到结果
#pragma mark--扫描成功 二维码代理--
/**
* 解析出元数据就会调用
*
* @param captureOutput 输出
* @param metadataObjects 元数据数组
* @param connection 连接
/
- (void)captureOutput(AVCaptureOutput)captureOutput didOutputMetadataObjects:(NSArray)metadataObjects fromConnection:(AVCaptureConnection)connection
{
//结束扫描
[self stopScan];
NSMutableString* str = [NSMutableString string];
for (AVMetadataMachineReadableCodeObject* objc in metadataObjects) {
[str appendString:objc.stringValue];
}
//打印
NSLog(@"%@",(NSString *)str);
[self.navigationController popViewControllerAnimated:YES];
}
-结束扫描的代码
#pragma mark--结束扫描
- (void)stopScan
{
//停止会话
[self.session stopRunning];
//移除特殊的图层
[self.preView removeFromSuperview];
}-
接下来需要注意的就是preView的实现 向微信扫描一样,有一个半透明的遮罩,中间有一个透明区域,这个地方用layer的mask实现,扫描动画,使用定时器实现,上代码
@interface PreView ()
@property(nonatomic,strong)UIImageView bgImage;
//添加线
@property (nonatomic, strong) UIImageView lineImageView;
//定时器
@property (nonatomic, strong) CADisplayLink* displayLink;
@end@implementation PreView /** * layer * @return 返回AVCaptureVideoPreviewLayer 特殊的layer 可以展示输入设备采集到的信息 */ +(Class)layerClass{ return [AVCaptureVideoPreviewLayer class]; } -(AVCaptureVideoPreviewLayer *)previewLayer{ AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer; return layer; } -(void)setSession:(AVCaptureSession *)session{ _session = session; AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer; layer.session = session; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setMask]; } return self; } -(void)setMask{ //添加扫描线 self.lineImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"line"]]; self.lineImageView.frame = CGRectMake((kScreenWidth - 280) / 2, 100, 280, 2); [self addSubview:self.lineImageView]; //计时器 self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scanAnimation)]; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; //创建一个maskView UIView *maskView = [[UIView alloc]initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenHigh-64)]; //颜色设置透明 maskView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; [self addSubview:maskView]; //创建一个中间有镂空区域的path UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, kScreenWidth, kScreenHigh-64)]; [maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake((kScreenWidth-280)/2, 100-64, 280, 280) cornerRadius:1] bezierPathByReversingPath]]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init]; maskLayer.path = maskPath.CGPath; maskView.layer.mask = maskLayer; //添加扫描框 [self addSubview:self.bgImage]; _bgImage.frame = CGRectMake((kScreenWidth-280)/2, 100, 280, 280); } #pragma mark--扫描动画-- - (void)scanAnimation { CGRect frame = self.lineImageView.frame; if (self.lineImageView.frame.origin.y > 100 + 270) { frame.origin.y = 100; self.lineImageView.frame = frame; } else { frame.origin.y += 2; self.lineImageView.frame = frame; } } #pragma mark --懒加载 -(UIImageView *)bgImage{ if (!_bgImage) { _bgImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_scanbg"]]; } return _bgImage; } @end
差不多 这样就搞定了,需要注意的是preView的加载和移除 还有扫描方法的加载时间,发现如果Push出来的窗口,二维码界面打开会有一秒多延迟,建议加一个MBProgress过度,如果Model的话,就没有这个问题(执行扫描我在viewWillAppear中加载)
附上github地址:https://github.com/superHS/Demo.git