在ios7蘋果推出了二維碼掃描,以前想要做二維碼掃描,只能通過第三方ZBar與ZXing。
ZBar在掃描的靈敏度上,和內存的使用上相對於ZXing上都是較優的,但是對於 “圓角二維碼” 的掃描確很困難。
ZXing 是 Google Code上的一個開源的條形碼掃描庫,是用java設計的,連Google Glass 都在使用的。但有人為了追求更高效率以及可移植性,出現了c++ port. Github上的Objectivc-C port,其實就是用OC代碼封裝了一下而已,而且已經停止維護。這樣效率非常低,在instrument下面可以看到CPU和內存瘋漲,在內存小的機器上很容易崩潰。
AVFoundation無論在掃描靈敏度和性能上來說都是最優的。
首先要導入#import <AVFoundation/AVFoundation.h>框架
完成二維碼掃描大致有十個步驟:
// 1.獲取輸入設備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2.創建輸入對象
NSError *error;
AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
if (inPut == nil) {
UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"設備不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
[self.view addSubview:aler];
[aler show];
return;
}
// 3.創建輸出對象
AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
// 4.設置代理監聽輸出對象的輸出流 (說明:使用主線程隊列,相應比較同步,使用其他隊列,相應不同步,容易讓用戶產生不好的體驗)
[outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 5.創建會話
AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.session = session;
// 6.將輸入和輸出對象添加到會話
if ([session canAddInput:inPut]) {
[session addInput:inPut];
}
if ([session canAddOutput:outPut]) {
[session addOutput:outPut];
}
// 7.告訴輸出對象, 需要輸出什麼樣的數據 // 提示:一定要先設置會話的輸出為output之後,再指定輸出的元數據類型!
[outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
// 8.創建預覽圖層
AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
preViewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:preViewLayer atIndex:0];
// 9.設置掃面范圍
outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
// 10.設置掃描框
UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
self.boxView = boxView;
boxView.layer.borderColor = [UIColor yellowColor].CGColor;
boxView.layer.borderWidth = 3;
[self.view addSubview:boxView];
// 設置掃描線
CALayer *scanLayer = [[CALayer alloc] init];
self.scanLayer = scanLayer;
scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2);
scanLayer.backgroundColor = [UIColor redColor].CGColor;
[boxView.layer addSublayer:scanLayer];
// 開始掃描
[session startRunning];
其中第9個步驟是可以優化內存的
@property(nonatomic) CGRect rectOfInterest;
這個屬性大致意思就是告訴系統它需要注意的區域,大部分APP的掃碼UI中都會有一個框,提醒你將條形碼放入那個區域,這個屬性的作用就在這裡,它可以設置一個范圍,只處理在這個范圍內捕獲到的圖像的信息。如此一來,我們代碼的效率又會得到很大的提高,在使用這個屬性的時候。需要幾點注意:
1、這個CGRect參數和普通的Rect范圍不太一樣,它的四個值的范圍都是0-1,表示比例。
2、經過測試發現,這個參數裡面的x對應的恰恰是距離左上角的垂直距離,y對應的是距離左上角的水平距離。
3、寬度和高度設置的情況也是類似。
-----------------------------以下是源碼:
#import "ScanQrcodeVController.h"
@protocol ScanQrcodeVControllerDelegate <NSObject> // 二維碼返回結果 -(void)scanQrcodeWithNString:(NSString *) ruselt; @end @interface ScanQrcodeVController : UIViewController @property (nonatomic, weak) id<ScanQrcodeVControllerDelegate>delegate; @end
#import "ScanQrcodeVController.m"
@interface ScanQrcodeVController ()<AVCaptureMetadataOutputObjectsDelegate>
// 會話
@property (nonatomic, strong) AVCaptureSession *session;
// 定時器
@property (nonatomic, strong) CADisplayLink *link;
// 掃描線
@property (nonatomic, strong) CALayer *scanLayer;
// 掃描框
@property (nonatomic, weak) UIView *boxView;
/// 保存二維碼結果
@property (nonatomic, copy) NSString *string;
@end
@implementation ScanQrcodeVController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"NavBack"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"確定" style:UIBarButtonItemStylePlain target:self action:@selector(doneClick)];
[self scanCode];
}
-(void)scanCode {
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataFrame)];
self.link = link;
link.frameInterval = 3;
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
// 1.獲取輸入設備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2.創建輸入對象
NSError *error;
AVCaptureDeviceInput *inPut = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
if (inPut == nil) {
UIAlertView *aler = [[UIAlertView alloc] initWithTitle:@"提示" message:@"設備不可用" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"確定", nil];
[self.view addSubview:aler];
[aler show];
return;
}
// 3.創建輸出對象
AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
// 4.設置代理監聽輸出對象的輸出流 說明:使用主線程隊列,相應比較同步,使用其他隊列,相應不同步,容易讓用戶產生不好的體驗
[outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 5.創建會話
AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.session = session;
// 6.將輸入和輸出對象添加到會話
if ([session canAddInput:inPut]) {
[session addInput:inPut];
}
if ([session canAddOutput:outPut]) {
[session addOutput:outPut];
}
// 7.告訴輸出對象, 需要輸出什麼樣的數據 // 提示:一定要先設置會話的輸出為output之後,再指定輸出的元數據類型!
[outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
// 8.創建預覽圖層
AVCaptureVideoPreviewLayer *preViewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
preViewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:preViewLayer atIndex:0];
// 9.設置掃面范圍
outPut.rectOfInterest = CGRectMake(0.2, 0.18, 0.6, 0.5);
// 10.設置掃描框
UIView *boxView = [[UIView alloc] initWithFrame:CGRectMake(0.2 * SrceenW, 0.18 * SrceenH, 0.6 * SrceenW, 0.5 * SrceenH)];
self.boxView = boxView;
boxView.layer.borderColor = [UIColor yellowColor].CGColor;
boxView.layer.borderWidth = 3;
[self.view addSubview:boxView];
// 設置掃描線
CALayer *scanLayer = [[CALayer alloc] init];
self.scanLayer = scanLayer;
scanLayer.frame = CGRectMake(0, 0, boxView.bounds.size.width, 2);
scanLayer.backgroundColor = [UIColor redColor].CGColor;
[boxView.layer addSublayer:scanLayer];
// 開始掃描
[session startRunning];
}
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
if (metadataObjects.count > 0) {
// 停止掃描
[self.session stopRunning];
// 移除CADisplayLink對象
[self.link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.link = nil;
// 取出數據
AVMetadataMachineReadableCodeObject *obj = [metadataObjects lastObject];
self.string = obj.stringValue;
}
NSLog(@"掃描--%@",self.string);
[NSThread sleepForTimeInterval:1.0];
}
-(void)updataFrame {
CGRect frame = self.scanLayer.frame;
if (self.scanLayer.frame.origin.y > self.boxView.frame.size.height) {
frame.origin.y = -20;
self.scanLayer.frame = frame;
}else{
frame.origin.y += 3;
self.scanLayer.frame = frame;
}
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
// 記得釋放CADisplayLink對象
if (self.link != nil) {
[self.link invalidate];
self.link = nil;
}
}
// 返回上一個界面
-(void)goBack {
[self.navigationController popViewControllerAnimated:YES];
}
// 二維碼掃描完成
-(void)doneClick {
// 設置代理
if ([self.delegate respondsToSelector:@selector(scanQrcodeWithNString:)]) {
[self.delegate scanQrcodeWithNString:self.string];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
@end