在app開發(fā)中,經(jīng)常會(huì)遇到二維碼掃描的功能。
今天我給大家介紹一下,iOS原生一維碼、二維碼掃描使用。簡(jiǎn)單實(shí)用
話不多說(shuō),直接上單代碼
原生二維碼掃描使用到<AVFoundation/AVFoundation.h>
在ViewController.m中:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "SecondViewController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface ViewController ()<UITabBarDelegate,AVCaptureMetadataOutputObjectsDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property ( strong , nonatomic ) AVCaptureDevice * device;
@property ( strong , nonatomic ) AVCaptureDeviceInput * input;
@property ( strong , nonatomic ) AVCaptureMetadataOutput * output;
@property ( strong , nonatomic ) AVCaptureSession * session;
@property ( strong , nonatomic ) AVCaptureVideoPreviewLayer * previewLayer;
/*** 專門用于保存描邊的圖層 ***/
@property (nonatomic,strong) CALayer *containerLayer;
@property(strong,nonatomic)UIView *customContainerView;
@property(strong,nonatomic)UILabel *customLabel;
@property(strong,nonatomic)UILabel *customLab;
@property(strong,nonatomic)UIButton *commodityBtn;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.view.alpha = 1;
self.customContainerView = [[UIView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH/5, SCREEN_HEIGHT/4, SCREEN_WIDTH*3/5, SCREEN_WIDTH*3/5)];
[self.customContainerView.layer setBorderWidth:2.0];
self.customContainerView.layer.borderColor = [UIColor colorWithRed:174/255.0 green:109/255.0 blue:45/255.0 alpha:1].CGColor;
// self.customContainerView.backgroundColor = [UIColor redColor];
[self.view addSubview:_customContainerView];
self.customLab = [[UILabel alloc]initWithFrame:CGRectMake(SCREEN_WIDTH/5, SCREEN_HEIGHT/4+SCREEN_WIDTH*3/5+10, SCREEN_WIDTH*3/5, 30)];
self.customLab.text = @"將二維碼/條形碼放入框內(nèi),即可自動(dòng)掃描";
self.customLab.font = [UIFont systemFontOfSize:11.0];
self.customLab.textAlignment = NSTextAlignmentCenter;
self.customLab.textColor = [UIColor whiteColor];
self.customLab.backgroundColor = [UIColor clearColor];
[self.view addSubview:_customLab];
self.customLabel = [[UILabel alloc]init];
self.commodityBtn = [[UIButton alloc]initWithFrame:CGRectMake(SCREEN_WIDTH/2-45, SCREEN_HEIGHT*3/4, 90, 30)];
self.commodityBtn.backgroundColor = [UIColor lightGrayColor];
[self.commodityBtn addTarget:self action:@selector(commodity:) forControlEvents:UIControlEventTouchUpInside];
[self.commodityBtn setTitle:@"直接錄入" forState:UIControlStateNormal];
self.commodityBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
[self.view addSubview:_commodityBtn];
// 開始掃描二維碼
[self startScan];
}
- (void)commodity:(UIButton *)sender{
SecondViewController *svc = [[SecondViewController alloc]init];
[self presentViewController:svc animated:YES completion:nil];
}
- (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];
}
return _previewLayer;
}
// 設(shè)置輸出對(duì)象解析數(shù)據(jù)時(shí)感興趣的范圍
// 默認(rèn)值是 CGRect(x: 0, y: 0, width: 1, height: 1)
// 通過(guò)對(duì)這個(gè)值的觀察, 我們發(fā)現(xiàn)傳入的是比例
// 注意: 參照是以橫屏的左上角作為, 而不是以豎屏
// out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
- (AVCaptureMetadataOutput *)output
{
if (_output == nil) {
_output = [[AVCaptureMetadataOutput alloc] init];
// _output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
// 1.獲取屏幕的frame
CGRect viewRect = self.view.frame;
// 2.獲取掃描容器的frame
CGRect containerRect = self.customContainerView.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;
// CGRect outRect = CGRectMake(x, y, width, height);
// [_output rectForMetadataOutputRectOfInterest:outRect];
_output.rectOfInterest = CGRectMake(x, y, width, height);
// _output.rectOfInterest = CGRectMake(SCREEN_WIDTH/4, SCREEN_HEIGHT/3, SCREEN_WIDTH/2, SCREEN_WIDTH/2);
}
return _output;
}
- (CALayer *)containerLayer
{
if (_containerLayer == nil) {
_containerLayer = [[CALayer alloc] init];
}
return _containerLayer;
}
- (void)startScan
{
// 1.判斷輸入能否添加到會(huì)話中
if (![self.session canAddInput:self.input]) return;
[self.session addInput:self.input];
// 2.判斷輸出能夠添加到會(huì)話中
if (![self.session canAddOutput:self.output]) return;
[self.session addOutput:self.output];
// 4.設(shè)置輸出能夠解析的數(shù)據(jù)類型
// 注意點(diǎn): 設(shè)置數(shù)據(jù)類型一定要在輸出對(duì)象添加到會(huì)話之后才能設(shè)置
self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes;
// 5.設(shè)置監(jiān)聽監(jiān)聽輸出解析到的數(shù)據(jù)
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 6.添加預(yù)覽圖層
[self.view.layer insertSublayer:self.previewLayer atIndex:0];
self.previewLayer.frame = self.view.bounds;
// 7.添加容器圖層
[self.view.layer addSublayer:self.containerLayer];
self.containerLayer.frame = self.view.bounds;
// 8.開始掃描
[self.session startRunning];
}
- (void)stopRunning{
[self.session stopRunning];
NSLog(@"%@",_customLabel.text);
SecondViewController *svc = [[SecondViewController alloc]init];
svc.str = _customLabel.text;
[self presentViewController:svc animated:YES completion:nil];
}
- (void)viewWillAppear:(BOOL)animated{
[self.session startRunning];
}
#pragma mark --------AVCaptureMetadataOutputObjectsDelegate ---------
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
// id 類型不能點(diǎn)語(yǔ)法,所以要先去取出數(shù)組中對(duì)象
AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
if (object == nil) return;
// 只要掃描到結(jié)果就會(huì)調(diào)用
self.customLabel.text = object.stringValue;
if (!(_customLabel.text == nil)) {
[self stopRunning];
}
// 清除之前的描邊
[self clearLayers];
// 對(duì)掃描到的二維碼進(jìn)行描邊
AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object];
// 繪制描邊
[self drawLine:obj];
}
- (void)drawLine:(AVMetadataMachineReadableCodeObject *)objc
{
NSArray *array = objc.corners;
// 1.創(chuàng)建形狀圖層, 用于保存繪制的矩形
CAShapeLayer *layer = [[CAShapeLayer alloc]init];
// 設(shè)置線寬
layer.lineWidth = 2;
// 設(shè)置描邊顏色
layer.strokeColor = [UIColor colorWithRed:174/255.0 green:109/255.0 blue:45/255.0 alpha:1].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
// 2.創(chuàng)建UIBezierPath, 繪制矩形
UIBezierPath *path = [[UIBezierPath alloc] init];
CGPoint point = CGPointZero;
int index = 0;
CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[index++]);
// 把點(diǎn)轉(zhuǎn)換為不可變字典
// 把字典轉(zhuǎn)換為點(diǎn),存在point里,成功返回true 其他false
CGPointMakeWithDictionaryRepresentation(dict, &point);
// 設(shè)置起點(diǎn)
[path moveToPoint:point];
// 2.2連接其它線段
for (int i = 1; i<array.count; i++) {
CGPointMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)array[i], &point);
[path addLineToPoint:point];
}
// 2.3關(guān)閉路徑
[path closePath];
layer.path = path.CGPath;
// 3.將用于保存矩形的圖層添加到界面上
[self.containerLayer addSublayer:layer];
}
- (void)clearLayers
{
if (self.containerLayer.sublayers)
{
for (CALayer *subLayer in self.containerLayer.sublayers)
{
[subLayer removeFromSuperlayer];
}
}
}
@end
在SecondViewController.h中:
#import <UIKit/UIKit.h>
@interface SecondViewController : UIViewController
@property(strong,nonatomic)NSString *str;
@end
在SecondViewController.m中:
#import "SecondViewController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface SecondViewController ()
@property(strong,nonatomic)UILabel *titleLab;
@property(strong,nonatomic)UILabel *contentLab;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self titleLabel];
[self contentLabel];
//返回按鈕
UIButton *backBtn = [[UIButton alloc]initWithFrame:CGRectMake(20, 100, 50, 35)];
[backBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
backBtn.backgroundColor = [UIColor lightGrayColor];
[backBtn setTitle:@"返回" forState:0];
[self.view addSubview:backBtn];
}
- (void)back{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)titleLabel{
self.titleLab = [[UILabel alloc]initWithFrame:CGRectMake(20, 200, SCREEN_WIDTH/2, 30)];
self.titleLab.text = @"掃描獲取的數(shù)據(jù)";
[self.view addSubview:_titleLab];
}
- (void)contentLabel{
self.contentLab = [[UILabel alloc]initWithFrame:CGRectMake(20, 245, SCREEN_WIDTH-40, 30)];
self.contentLab.text = _str;
[self.view addSubview:_contentLab];
}
@end
見面比較簡(jiǎn)單,可以根據(jù)自自己的需求進(jìn)行點(diǎn)綴,一維碼、二維碼掃描功能都可以使用。
代碼后運(yùn)行報(bào)如下的錯(cuò)誤:
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.
plist必須包含一個(gè)NSCameraUsageDescription,需要去plist中添加。