掃描功能小結 (掃描二維碼、條碼),掃描功能小結條碼
此次掃碼功能以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; // 不置空,會有null 指標,由下個介面返回時會出現崩潰 /* 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
建立對象