我以為還要去找第三方套件來用,一查網路才發現,從iOS 7開始,SDK已經內建有掃描條碼的API,我想會把這功能納入麾下是必然的,因為在生活中條碼無所不在,除了常見的用途如辨識商品(書籍、食品、識別證等)外,還能做非常有趣的應用(導覽、遊戲等)。
現在就來學一下怎麼掃描QR Code和Bar Code吧!
掃描條碼的API被收錄在AVFoundation Framework,目前可支援以下規格條碼:
- UPC-A
- UPC-E
- Code 39
- Code 39 mod 43
- Code 93
- Code 128
- EAN-8
- EAN-13
- Aztec
- PDF417
- QR
我們直接來寫程式吧~
/**
Theme: Scan QR Code & Bar Code
IDE: Xcode 7
Language: Objective C
Date: 104/11/19
Author: HappyMan
Blog: https://cg2010studio.wordpress.com/
*/
#import <AVFoundation/AVFoundation.h>
@interface TRCRecordRegisterViewController : UIViewController <AVCaptureMetadataOutputObjectsDelegate>
{
IBOutlet UILabel *statementLabel;
IBOutlet UIView *scanView;
}
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
@property (nonatomic) BOOL isReading;
-(BOOL)startReading;
-(void)stopReading;
@end
@implementation TRCRecordRegisterViewController
#pragma mark - IBAction method implementation
- (void)viewDidLoad {
[super viewDidLoad];
// Initially make the captureSession object nil.
_captureSession = nil;
// Set the initial value of the flag to NO.
_isReading = NO;
[self startStopReading:nil];
}
- (IBAction)startStopReading:(id)sender {
if (!_isReading) {
// This is the case where the app should read a QR code when the start button is tapped.
if ([self startReading]) {
// If the startReading methods returns YES and the capture session is successfully
// running, then change the start button title and the status message.
}
}
else{
// In this case the app is currently reading a QR code and it should stop doing so.
[self stopReading];
// The bar button item's title should change again.
}
// Set to the flag the exact opposite value of the one that currently has.
_isReading = !_isReading;
}
#pragma mark - Private method implementation
- (BOOL)startReading {
NSError *error;
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video
// as the media type parameter.
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&amp;error];
if (!input) {
// If any error occurs, simply log the description of it and don't continue any more.
NSLog(@"%@", [error localizedDescription]);
return NO;
}
// Initialize the captureSession object.
_captureSession = [[AVCaptureSession alloc] init];
// Set the input device on the capture session.
[_captureSession addInput:input];
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
[_captureSession addOutput:captureMetadataOutput];
// Create a new serial dispatch queue.
dispatch_queue_t dispatchQueue;
dispatchQueue = dispatch_queue_create("myQueue", NULL);
[captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
[captureMetadataOutput setMetadataObjectTypes:[captureMetadataOutput availableMetadataObjectTypes]];
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
_videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[_videoPreviewLayer setFrame:scanView.layer.bounds];
[scanView.layer addSublayer:_videoPreviewLayer];
// Start video capture.
[_captureSession startRunning];
return YES;
}
-(void)stopReading{
// Stop video capture and make the capture session object nil.
[_captureSession stopRunning];
_captureSession = nil;
// Remove the video preview layer from the viewPreview view's layer.
[_videoPreviewLayer removeFromSuperlayer];
}
#pragma mark - AVCaptureMetadataOutputObjectsDelegate method implementation
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
// Check if the metadataObjects array is not nil and it contains at least one object.
if (metadataObjects != nil &amp;&amp; [metadataObjects count] &gt; 0) {
NSLog(@"Result:%@", metadataObjects);
// Get the metadata object.
AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeCode93Code] || [[metadataObj type] isEqualToString:AVMetadataObjectTypeCode128Code]) {
// If the found metadata is equal to the QR code metadata then update the status label's text,
// stop reading and change the bar button item's title and the flag's value.
// Everything is done on the main thread.
// 掃瞄完畢停止重複掃描
[self stopReading];
[self performSelectorOnMainThread:@selector(stopReading) withObject:nil waitUntilDone:NO];
_isReading = NO;
}
}
}
@end
以上程式碼需配合IB來實現,呈現的介面如下~
我在程式中設定可以掃所有支援的條碼:
- [captureMetadataOutput availableMetadataObjectTypes]
而在掃瞄到的條碼只對以下兩種做處理:
- AVMetadataObjectTypeCode93Code
- AVMetadataObjectTypeCode128Code
如果想要處理QR Code,可以使用:
- AVMetadataObjectTypeQRCode
實際操作如首圖以及下圖:
掃到後會得到分別如下的資訊:
Result:(
“<AVMetadataMachineReadableCodeObject: 0x15055e4c0, type=\"org.iso.QRCode\", bounds={ 0.4,0.5 0.2×0.3 }>corners { 0.4,0.8 0.5,0.9 0.5,0.6 0.4,0.5 }, time 126077075932541, stringValue \"https://cg2010studio.wordpress.com\""
)
Result:(
“<AVMetadataMachineReadableCodeObject: 0x14ed975e0, type=\"org.iso.Code39\", bounds={ 0.2,0.4 0.4×0.1 }>corners { 0.6,0.5 0.6,0.5 0.2,0.4 0.2,0.4 }, time 126242691839958, stringValue \"/OIZHBKQ\""
)
可以試試看掃不是條碼的東西,可能會有很奇妙的發現喔:P~
最近在提攜公司新人,跟他說明很多功能可以找現成的來套,如此能省下非常多的時間,以便把火力集中在核心功能上!什麼是核心功能呢?要是我能做自家的產品,就自己來研發一套演算法流程吧!
參考:How To Scan QR Code Using AVFoundation Framework、Scanning Barcodes with iOS 7 (Objective-C)。


Comments on: "[iOS] 掃描QR Code和Bar Code" (8)
請問您如何實現除了紅色筐以外都是模糊透明的呢?
讚讚
我想到的有兩種方式:
1使用有透明度影像格式製作
2撰寫繪圖程式碼
我個人偏好第1項!對我來說比較簡單~ 😀
讚讚
[…] 過去嘗試過用相機掃描QRCode,現在想要讀取影像中QRCode,可以怎麼做呢? […]
讚讚
[…] SDK呼叫method來掃描QRCode,那麼是否也能從iOS SDK來產生 QRCode (QRCode […]
讚讚
您好,想請教一下,如果想在掃QRCode那個正方型外面的地方加個像相框的view是可行的嗎?
讚Liked by 1 person
當然可以囉!只是把相框加在螢幕上方,而QRcode依然會被相機掃描到。
讚讚
不好意思,想問一下,為什麼我會一直出錯:
[MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
[MC] Reading from public effective user settings.
我已經有在info裡加上
NSCameraUsageDescription
$(PRODUCT_NAME) uses camera
還是一樣一直報錯…
讚讚
[…] SDK內建方法來掃描QR Code和Bar Code。然而似乎還無法產生QR Code和Bar […]
讚讚