扫描功能小结 (扫描二维码、条形码)
此次扫码功能以iOS系统原生的AVFoundation框架为基础。
废话不多说,直接上代码
#import <UIKit/UIKit.h> @interface ScanViewController : UIViewController @end
在.m文件中创建对象
#import "ScanViewController.h" #import <AVFoundation/AVFoundation.h> #import "UserInputCodeViewController.h" #import "ScanResultViewController.h" @interface ScanViewController ()<AVCaptureMetadataOutputObjectsDelegate> @property (nonatomic, strong) AVCaptureDevice *device; @property (nonatomic, strong) AVCaptureDeviceInput *input; @property (nonatomic, strong) AVCaptureMetadataOutput *output; @property (nonatomic, strong) AVCaptureSession *session; @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; /*** 专门用于保存描边的图层 ***/ @property (nonatomic,strong) CALayer *containerLayer; /** 扫描范围 */ @property(nonatomic, strong) UIView *scanFrame; @property(nonatomic, strong) UIView *line; //@property (nonatomic, strong) UIImageView *scanArea; @property (nonatomic, strong) UILabel *lightLb; /** 是否打开灯光 */ @property (nonatomic, assign) BOOL isLightOn; @end @implementation ScanViewController -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (!_session ) { [self startScan]; } [self refreshScanLineAnimation]; } -(void)refreshScanLineAnimation { if (_line) { [self scanLineAnimate]; } } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.title = @"扫码开锁"; self.view.backgroundColor = [UIColor whiteColor]; _isLightOn = NO; [self initViews]; [self initConner]; [self startScan]; } -(void)initViews { CGRect frame = CGRectZero; frame.origin.x = (SCREEN_WIDTH - 230) / 2; frame.origin.y = (SCREEN_HEIGHT - 500) / 2; frame.size.width = 230; frame.size.height= 230; UIView* view = [[UIView alloc] initWithFrame:frame]; view.backgroundColor= [UIColor clearColor]; view.layer.borderWidth = 1; view.layer.borderColor = [[UIColor grayColor] CGColor]; self.scanFrame = view; [self.view addSubview:view]; //top UIView* shadow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, view.frame.origin.y)]; shadow.backgroundColor = [UIColor blackColor]; shadow.alpha = 0.5; [self.view addSubview:shadow]; //bottom shadow = [[UIView alloc] initWithFrame:CGRectMake(0, view.frame.origin.y + view.frame.size.height, SCREEN_WIDTH, SCREEN_HEIGHT - (view.frame.origin.y + view.frame.size.height))]; shadow.backgroundColor = [UIColor blackColor]; shadow.alpha = 0.5; [self.view addSubview:shadow]; //left shadow = [[UIView alloc] initWithFrame:CGRectMake(0, view.frame.origin.y, (SCREEN_WIDTH - view.frame.size.width) / 2, view.frame.size.height)]; shadow.backgroundColor = [UIColor blackColor]; shadow.alpha = 0.5; [self.view addSubview:shadow]; //right shadow = [[UIView alloc] initWithFrame:CGRectMake(view.frame.origin.x + view.frame.size.width, view.frame.origin.y, (SCREEN_WIDTH - view.frame.size.width) / 2, view.frame.size.height)]; shadow.backgroundColor = [UIColor blackColor]; shadow.alpha = 0.5; [self.view addSubview:shadow]; _line = [[UIView alloc] initWithFrame:CGRectMake(view.frame.origin.x + 1, view.frame.origin.y + 1, frame.size.width - 2, 3)]; _line.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:_line]; UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(view.frame) + HEIGTHCUSTOME(70), _line.frame.size.width, 15)]; label.font = UIFontWithPX(26); label.textColor = [UIColor whiteColor]; label.textAlignment = NSTextAlignmentCenter; label.center = CGPointMake(_line.center.x, label.center.y); label.text = @"请对准充电箱上的二维码"; [self.view addSubview:label]; //light UIButton* light = [UIButton buttonWithType:UIButtonTypeCustom]; light.frame = CGRectMake(0, view.frame.origin.y + view.frame.size.height + 120, 100, 100); // light.center = CGPointMake(view.frame.origin.x + view.frame.size.width - 50, light.center.y); light.center = CGPointMake(view.center.x, light.center.y); light.backgroundColor = [UIColor clearColor]; [light setImage:IMAGE(@"flashlight_off") forState:UIControlStateNormal]; [light addTarget:self action:@selector(openLight:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:light]; _lightLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)]; _lightLb.center = CGPointMake(light.center.x, light.center.y + 50); _lightLb.text = @"打开手电筒"; _lightLb.textAlignment = NSTextAlignmentCenter; _lightLb.textColor = [UIColor whiteColor]; _lightLb.font = [UIFont systemFontOfSize:14]; [self.view addSubview:_lightLb]; } - (void)initConner { CGFloat w = 2; CGFloat h = 10; CGFloat d = 2; CGPoint point = CGPointZero; //left - top point = CGPointMake(_scanFrame.frame.origin.x - w - d, _scanFrame.frame.origin.y - w - d); UIView* view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, w, h)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, h, w)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; //left - down point = CGPointMake(_scanFrame.frame.origin.x - w - d, _scanFrame.frame.origin.y + _scanFrame.frame.size.height + d + w - h); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, w, h)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; point = CGPointMake(point.x, _scanFrame.frame.origin.y + _scanFrame.frame.size.height + d); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, h, w)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; //right - top point = CGPointMake(_scanFrame.frame.origin.x + _scanFrame.frame.size.width + d, _scanFrame.frame.origin.y - w - d); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, w, h)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; point = CGPointMake(_scanFrame.frame.origin.x + _scanFrame.frame.size.width + w + d - h, point.y); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, h, w)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; //right - down point = CGPointMake(_scanFrame.frame.origin.x + _scanFrame.frame.size.width + d, _scanFrame.frame.origin.y + _scanFrame.frame.size.height + w + d - h); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, w, h)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; point = CGPointMake(point.x + w - h, _scanFrame.frame.origin.y + _scanFrame.frame.size.height + d); view = [[UIView alloc] initWithFrame:CGRectMake(point.x, point.y, h, w)]; view.backgroundColor = UIColorFromRGB(0x28a0f0); [self.view addSubview:view]; } - (void)scanLineAnimate { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:2]; [UIView setAnimationCurve:UIViewAnimationCurveLinear]; [UIView setAnimationRepeatAutoreverses:YES]; [UIView setAnimationRepeatCount:HUGE_VALF]; _line.center = CGPointMake(_line.center.x, _scanFrame.frame.origin.y + _scanFrame.frame.size.height - 5); [UIView commitAnimations]; } - (void)openLight:(UIButton*)sender { [_device lockForConfiguration:nil]; if (!sender.selected) { [sender setImage:IMAGE(@"flashlight_on") forState:UIControlStateNormal]; [_device setTorchMode:AVCaptureTorchModeOn]; [_device setFlashMode:AVCaptureFlashModeOn]; _lightLb.text = @"关闭手电筒"; sender.selected = YES; } else { [sender setImage:IMAGE(@"flashlight_off") forState:UIControlStateNormal]; [_device setTorchMode:AVCaptureTorchModeOff]; [_device setFlashMode:AVCaptureFlashModeOff]; _lightLb.text = @"打开手电筒"; sender.selected = NO; } [_device unlockForConfiguration]; } - (void)startScan { if (![self.session canAddInput:self.input]) return; [self.session addInput:self.input]; if (![self.session canAddOutput:self.output]) return; [self.session addOutput:self.output]; // self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes; // 如此设置,可扫描二维码、条形码 [self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; [self.view.layer insertSublayer:self.previewLayer atIndex:0]; [self.view.layer addSublayer:self.containerLayer]; [self.session startRunning]; } - (void)drawLine:(AVMetadataMachineReadableCodeObject *)objc { NSArray *array = objc.corners; CAShapeLayer *layer = [[CAShapeLayer alloc] init]; layer.lineWidth = 2; layer.strokeColor = [UIColor greenColor].CGColor; layer.fillColor = [UIColor clearColor].CGColor; UIBezierPath *path = [[UIBezierPath alloc] init]; CGPoint point = CGPointZero; int index = 0; CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[index++]); CGPointMakeWithDictionaryRepresentation(dict, &point); [path moveToPoint:point]; for (int i = 1; i<array.count; i++) { CGPointMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)array[i], &point); [path addLineToPoint:point]; } [path closePath]; layer.path = path.CGPath; [self.containerLayer addSublayer:layer]; } - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (_session) { [_session stopRunning]; } [_output setMetadataObjectsDelegate:nil queue:dispatch_get_main_queue()]; // id 类型不能点语法,所以要先去取出数组中对象 id object = [metadataObjects lastObject]; if ([object isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) { NSString *stringValue; if ([metadataObjects count] >0) { AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects lastObject]; stringValue = metadataObject.stringValue; } [self judgeCode:stringValue]; // [self judgeCode:@"test00001"]; [self clearLayers]; AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object]; [self drawLine:obj]; } } // 初始化扫描界面 -(void)initPreviewImg { [_output setMetadataObjectsDelegate:nil queue:dispatch_get_main_queue()]; [_session stopRunning]; [_session removeOutput:_output]; [_session removeInput:_input]; [_containerLayer removeFromSuperlayer]; [_previewLayer removeFromSuperlayer]; _containerLayer = nil; //不置空,二维码的彩色边框无法去除 _session = nil; // 不置空,返回本页面,出现白屏 _output = nil; _previewLayer = nil; // 不置空,会有空指针,由下个界面返回时会出现崩溃 /* Assertion failed: (_inputInternal->figCaptureSession == NULL), function __44-[AVCaptureInput attachToFigCaptureSession:]_block_invoke, file /BuildRoot/Library/Caches/com.apple.xbs/Sources/EmbeddedAVFoundation/EmbeddedAVFoundation-1184.7/Aspen/AVCaptureInput.m, line 132. */ } - (void)clearLayers { if (self.containerLayer.sublayers) { for (CALayer *subLayer in self.containerLayer.sublayers) { [subLayer removeFromSuperlayer]; } } } #pragma mark -------- 懒加载--------- - (AVCaptureDevice *)device { if (_device == nil) { _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; } return _device; } - (AVCaptureDeviceInput *)input { if (_input == nil) { _input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil]; } return _input; } - (AVCaptureSession *)session { if (_session == nil) { _session = [[AVCaptureSession alloc] init]; } return _session; } - (AVCaptureVideoPreviewLayer *)previewLayer { if (_previewLayer == nil) { _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; _previewLayer.frame = self.view.bounds; } return _previewLayer; } - (AVCaptureMetadataOutput *)output { if (_output == nil) { _output = [[AVCaptureMetadataOutput alloc] init]; CGRect viewRect = self.view.frame; CGRect containerRect = _scanFrame.frame; CGFloat x = containerRect.origin.y / viewRect.size.height; CGFloat y = containerRect.origin.x / viewRect.size.width; CGFloat width = containerRect.size.height / viewRect.size.height; CGFloat height = containerRect.size.width / viewRect.size.width; _output.rectOfInterest = CGRectMake(x, y, width, height); } return _output; } - (CALayer *)containerLayer { if (_containerLayer == nil) { _containerLayer = [[CALayer alloc] init]; _containerLayer.frame = self.view.bounds; } return _containerLayer; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end