原文鏈接:http://blog.csdn.net/whjblog/article/details/15809859
iOS二維碼掃描開源庫主要有Zbar和ZXing,在完成項目的過程中都用過,開始時用了Zbar,但其相對於ZXing掃描識別效率太低,無法達到項目要求,所以改用ZXing。兩個開源庫都可以識別二維碼,但ZBar還支持條形碼,ZXing識別條形碼還需要改一些地方。
一、ZBar的使用
1.下載ZBar SDK,將ZBarSDK拖動到Xcode項目
下載地址:https://github.com/bmorton/ZBarSDK
2.添加framework框架
* AVFoundation.framework (weak) * CoreMedia.framework (weak) * CoreVideo.framework (weak) * QuartzCore.framework
3.掃描代碼
引入頭文件 #import "ZBarSDK.h" ,實現ZBarReaderDelegate協議
- ZBarReaderViewController *reader = [ZBarReaderViewController new];
- reader.readerDelegate = self;
- reader.supportedOrientationsMask = ZBarOrientationMaskAll;
- ZBarImageScanner *scanner = reader.scanner;
- [scanner setSymbology: ZBAR_I25
- config: ZBAR_CFG_ENABLE
- to: 0];
- [self presentModalViewController:reader animated:YES];
- [reader release];
掃描結果回調
- - (void) imagePickerController: (UIImagePickerController*) reader didFinishPickingMediaWithInfo: (NSDictionary*) info
- {
- id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
- ZBarSymbol *symbol = nil;
- for(symbol in results)
- break;
- text = symbol.data ;
- }
還可以在ZBarReaderViewController自定義掃描界面和掃描區域(scanCrop)。
二、ZXing的使用
1.下載ZXIng,下載地址: https://github.com/zxing/zxing .由於支持各種語言,整個工程比較大,IOS 只需保留cpp和iphone這2個文件夾,整個目錄結構不變,cpp文件夾只保留cpp/core/src/zxing下面的內容,其餘都可以全部刪除。然後把刪減好的zxing目錄移動到我們的iOS項目的目錄下(也可以右擊選擇Add
files to ‘project’),並在 zxing/iphone/ZXingWidget/
目錄下把ZXingWidget.xcodeproj
拖動到我們的項目工程中。
2.選擇 target ——>Build Phases 在 Link Binary with Libraries裏 添加libZXingWidget.a,在Target Dependencies添加
ZXingWidget
。在target ——>Build Settings ——>Headers Search Path 添加兩個路徑:
.
/zxing/iphone/ZXingWidget/Classes
./zxing/cpp/core/src
第一個設成recursive ,第二個設成non-recursive
添加一下frameworks 支持
- AVFoundation
- AudioToolbox
- CoreVideo
- CoreMedia
- libiconv
- AddressBook
- AddressBookUI
3.掃描代碼
(1)直接引用
頭文件:
#import <ZXingWidgetController.h>
#import <QRCodeReader.h>
掃描調用代碼:
- ZXingWidgetController *widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
- NSMutableSet *readers = [[NSMutableSet alloc] init];
- QRCodeReader *qrcodeReader = [[QRCodeReader alloc] init];
- [readers addObject:qrcodeReader];
- widController.readers = readers;
- [self presentViewController:widController animated:YES completion:^{}];
掃描結果回調:
- @protocol ZXingDelegate
- - (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result;
- - (void)zxingControllerDidCancel:(ZXingWidgetController*)controller;
- @end
還需要把引入的.m文件改爲.mm。
(2)自定義掃描
我的項目要求中需要自定義掃描界面和掃描區域,還要加入C語言混編,所以就用了這種方法。
頭文件:
- #import <AssetsLibrary/AssetsLibrary.h>
- #import <QRCodeReader.h>
- #import <TwoDDecoderResult.h>
- #import <Decoder.h>
初始化AVCapture
- - (void)initCapture
- {
- AVCaptureDevice* inputDevice =
- [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- AVCaptureDeviceInput *captureInput =
- [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:nil];
- AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];
- captureOutput.alwaysDiscardsLateVideoFrames = YES;
- [captureOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
- NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
- NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
- NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
- [captureOutput setVideoSettings:videoSettings];
- self.captureSession = [[[AVCaptureSession alloc] init] autorelease];
- NSString* preset = 0;
- if (NSClassFromString(@"NSOrderedSet") && // Proxy for "is this iOS 5" ...
- [UIScreen mainScreen].scale > 1 &&
- isIPad() &&
- [inputDevice
- supportsAVCaptureSessionPreset:AVCaptureSessionPresetiFrame960x540]) {
- preset = AVCaptureSessionPresetiFrame960x540;
- }
- if (!preset) {
- preset = AVCaptureSessionPresetMedium;
- }
- self.captureSession.sessionPreset = preset;
- [self.captureSession addInput:captureInput];
- [self.captureSession addOutput:captureOutput];
- [captureOutput release];
- if (!self.prevLayer) {
- self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
- }
- self.prevLayer.frame = self.view.bounds;
- self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
- [self.view.layer addSublayer: self.prevLayer];
- [self.captureSession startRunning];
- }
得到AVCapture回調函數
- - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
- {
- if (!isScanning||delegate==nil) {
- return;
- }
- if (sampleBuffer)
- {
- //將sampleBuffer轉化爲UIImage
- UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
- //用Decoder來解析識別圖像
- [self decodeImage:image];
- }
- }
- - (void)decodeImage:(UIImage *)image
- {
- NSMutableSet *qrReader = [[NSMutableSet alloc] init];
- QRCodeReader *qrcoderReader = [[QRCodeReader alloc] init];
- [qrReader addObject:qrcoderReader];
- [qrcoderReader release];
- Decoder *decoder = [[Decoder alloc] init];
- decoder.delegate = self;
- decoder.readers = qrReader;
- [decoder decodeImage:image];
- [decoder release];
- [qrReader release];
- }
- @optional
- - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset;
- - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)result;
- - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason;
- - (void)decoder:(Decoder *)decoder foundPossibleResultPoint:(CGPoint)point;
注意掃描的畫面不會跟着設備旋轉,當橫豎轉換是需要改變畫面
- CGFloat angle = 0.0f;
- switch (barOrientation)
- {
- case UIInterfaceOrientationPortrait:
- angle = 0.0f;
- break;
- case UIInterfaceOrientationPortraitUpsideDown:
- angle = M_PI;
- break;
- case UIInterfaceOrientationLandscapeLeft:
- angle = M_PI / 2;
- break;
- case UIInterfaceOrientationLandscapeRight:
- angle = -M_PI / 2;
- break;
- default:
- break;
- }
- CATransform3D transform = CATransform3DMakeRotation(angle, 0, 0, 1.0);
- self.captureVideoPreviewLayer.transform =transform;
- self.captureVideoPreviewLayer.frame = layerframe;