利用系统AV框架制作实现扫一扫功能(一行代码搞定)

1、先上图看看效果

Screen-Shot-2015-12-03-at-下午5.32.13

2、设计思路
2.1、利用AVCaptureMetadataOutput固有的输出属性,能够输出二维码及条形码的解析结果
2.2、利用Quartz 2D绘出基本扫描界面,结合动画效果,简单又高效!2.3、只需两个类,一个控制器,一个视图:SAScanCtrl SAScan
2.3、其他内容在代码中给出相关注释说明

3、部分代码
SAScanCtrl.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//
//? SAScanCtrl.m
//
//
//? Created by 余西安 on 15/12/3.
//? Copyright ? 2015年 Sian. All rights reserved.
//
?
#import "SAScanCtrl.h"
#import "SAScan.h"
#import <AVFoundation/AVFoundation.h>
?
@interface SAScanCtrl ()<AVCaptureMetadataOutputObjectsDelegate>
?
@property (nonatomic, strong) AVCaptureSession *session;
?
@property (nonatomic, strong) AVAudioPlayer??? *player;
?
@property (nonatomic, strong) SAScan?????????? *scan;
?
@end
?
@implementation SAScanCtrl
?
- (instancetype)initWithBlock:(SAScanBlock)block
{
????if (self = [super init]){
????????self.block = block;
????}
????return self;
}
?
- (void)viewDidLoad
{
????[super viewDidLoad];
????[self.navigationItem setTitle:@"扫一扫"];
?????
????AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
????AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
????AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
????[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];?? // 设置代理 在主线程里刷新
????[output setRectOfInterest:CGRectMake(0.12 , 0.12, 0.8, 0.8)];
?????
????//初始化链接对象
????self.session = [[AVCaptureSession alloc] init];
????[self.session addInput:input];
????[self.session addOutput:output];
????[self.session setSessionPreset:AVCaptureSessionPresetHigh];
????//设置扫码支持的编码格式(如下设置条形码和二维码兼容)
????[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]];
?????
????AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
????layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
????layer.frame = self.view.bounds;
?
????[self.view.layer insertSublayer:layer atIndex:0];
????self.scan = [[SAScan alloc] initWithFrame:self.view.bounds];
????[self.view addSubview:self.scan];
?????
????NSURL *url = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"scan" ofType:@"mp3"]];
????self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
?????
????UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"重试" style:UIBarButtonItemStylePlain target:self action:@selector(startRunning)];
????[self.navigationItem setRightBarButtonItem:item];
}
?
- (void)viewWillAppear:(BOOL)animated
{
????[super viewWillAppear:animated];
????[self.session startRunning];
}
?
- (void)viewDidAppear:(BOOL)animated
{
????[super viewDidAppear:animated];
????[self.scan startAnimation];
}
?
- (void)viewWillDisappear:(BOOL)animated
{
????[super viewWillDisappear:animated];
????[self.session stopRunning];
}
?
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
????if (metadataObjects.count > 0){
????????AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
????????[self.player play];
????????[self.scan stopAnimation];
????????[self.session stopRunning];
????????if (self.block) self.block(metadataObject.stringValue);
????????[self.navigationController popViewControllerAnimated:YES];
????????[self dismissViewControllerAnimated:YES completion:nil];
????}
}
?
- (void)startRunning
{
????[self.session startRunning];
????[self.scan startAnimation];
}
@end

Scan.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//
//? SAScan.m
//
//
//? Created by 余西安 on 15/12/3.
//? Copyright ? 2015年 Sian. All rights reserved.
//
?
#import "SAScan.h"
?
@implementation SAScan
?
- (instancetype)initWithFrame:(CGRect)frame
{
????if (self = [super initWithFrame:frame]){
????????self.backgroundColor = [UIColor clearColor];
????????UIImage *line = [UIImage imageNamed:@"scanLine.png"];
????????self.scanLine = [[UIImageView alloc] initWithImage:line];
????????[self.scanLine setHidden:YES];
????????[self addSubview:self.scanLine];
????}
????return self;
}
?
- (void)drawRect:(CGRect)rect
{
????CGContextRef context = UIGraphicsGetCurrentContext();
????//非扫码区域半透明
????{
????????CGFloat w = self.bounds.size.width * 0.7;
????????CGFloat x = (self.bounds.size.width - w) * 0.5;
????????CGFloat y = (self.bounds.size.height - w) * 0.5;
????????{?? // 外围区域填充半透明黑色
????????????// 设置非识别区域颜色
????????????CGContextSetRGBFillColor(context, 0, 0, 0, 0.6);
?????????????
????????????// 扫码区域上面填充
????????????CGRect rect = CGRectMake(0, 0, self.frame.size.width, y);
????????????CGContextFillRect(context, rect);
?????????????
????????????// 扫码区域左边填充
????????????rect = CGRectMake(0, y, x, w);
????????????CGContextFillRect(context, rect);
?????????????
????????????// 扫码区域右边填充
????????????rect = CGRectMake(x + w, y, x, w);
????????????CGContextFillRect(context, rect);
?????????????
????????????// 扫码区域下面填充
????????????rect = CGRectMake(0, y + w, self.frame.size.width, self.frame.size.height - y - w);
????????????CGContextFillRect(context, rect);
????????}
?????????
????????{?? // 中间可视区域画边框
????????????UIColor *whiteColor = [UIColor colorWithWhite:1.0 alpha:0.5];
????????????CGContextSetStrokeColorWithColor(context, whiteColor.CGColor);
????????????CGContextSetLineWidth(context, 1);
????????????CGContextAddRect(context, CGRectMake(x, y, w, w));
????????????CGContextStrokePath(context);
????????}
?????????
?????????
????????{?? // 中间可视区域画角框
????????????CGFloat lineWidth = 4.0f;
????????????CGFloat angleWidth = 15.0f;
????????????UIColor *greenColor = [UIColor colorWithRed:0 green:1.0 blue:0 alpha:0.8];
????????????CGContextSetLineWidth(context, lineWidth);
????????????CGContextSetStrokeColorWithColor(context, greenColor.CGColor);
?????????????
????????????// 左上角
????????????CGContextMoveToPoint(context, x, y + angleWidth);
????????????CGContextAddLineToPoint(context, x, y);
????????????CGContextAddLineToPoint(context, x + angleWidth, y);
????????????// 右上角
????????????CGContextMoveToPoint(context, x + w - angleWidth, y);
????????????CGContextAddLineToPoint(context, x + w, y);
????????????CGContextAddLineToPoint(context, x + w, y + angleWidth);
????????????// 右下角
????????????CGContextMoveToPoint(context, x + w, y + w - angleWidth);
????????????CGContextAddLineToPoint(context, x + w, y + w);
????????????CGContextAddLineToPoint(context, x + w - angleWidth, y + w);
????????????// 左下角
????????????CGContextMoveToPoint(context, x + angleWidth, y + w);
????????????CGContextAddLineToPoint(context, x, y + w);
????????????CGContextAddLineToPoint(context, x, y + w - angleWidth);
????????}
????????CGContextStrokePath(context);
????}
}
?
- (void)layoutSubviews
{
????[super layoutSubviews];
????CGFloat width = self.bounds.size.width;
????CGFloat y = (self.bounds.size.height - width * 0.7) * 0.5;
????self.scanLine.frame = CGRectMake(0, y, width, 12);
}
?
- (void)startAnimation
{
????CGFloat width = self.bounds.size.width;
????CGFloat y = (self.bounds.size.height - width * 0.7) * 0.5;
????self.scanLine.frame = CGRectMake(0, y, width, 12);
????self.scanLine.hidden = NO;
????[UIView animateWithDuration:2.5 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveLinear animations:^{
????????self.scanLine.center = CGPointMake(width * 0.5, y + width * 0.7);
????} completion:^(BOOL finished) {
????????if (finished) self.scanLine.center = CGPointMake(width * 0.5, y);
????}];
}
?
- (void)stopAnimation
{
????self.scanLine.hidden = YES;
????[self.scanLine.layer removeAllAnimations];
}
@end

4、使用只需一行代码

1
2
3
4
5
    SAScanCtrl *scan = [[SAScanCtrl alloc] initWithBlock:^(NSString *string) {
        [[[UIAlertView alloc] initWithTitle:nil message:string delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];
    }];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:scan];
    [self presentViewController:nav animated:YES completion:nil];

5、Demo下载

SAScan

Leave a Reply