一、效果預覽:
功能描述:WSLNativeScanTool是在利用原生API的條件下封裝的二維碼掃描工具,支持二維碼的掃描、識別圖中二維碼、生成自定義顏色和中心圖標的二維碼、監測環境亮度、打開閃光燈這些功能;WSLScanView是參照微信封裝的一個掃一掃界面,支持線條顏色、大小、動畫圖片、矩形掃描框樣式的自定義;這個示例本身就是仿照微信的掃一掃功能實現的。
二、實現
主要代碼如下:
- 初始化掃描識別對象
- (AVCaptureSession *)session{
if (_session == nil){
//獲取攝像設備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//創建輸入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if (!input){
return nil;
}
//創建二維碼掃描輸出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
//設置代理 在主線程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//設置采集掃描區域的比例 默認全屏是(0,0,1,1)
//rectOfInterest 填寫的是一個比例,輸出流視圖preview.frame為 x , y, w, h, 要設置的矩形快的scanFrame 為 x1, y1, w1, h1. 那么rectOfInterest 應該設置為 CGRectMake(y1/y, x1/x, h1/h, w1/w)。
CGFloat x = CGRectGetMinX(self.scanFrame)/ CGRectGetWidth(self.preview.frame);
CGFloat y = CGRectGetMinY(self.scanFrame)/ CGRectGetHeight(self.preview.frame);
CGFloat width = CGRectGetWidth(self.scanFrame)/ CGRectGetWidth(self.preview.frame);
CGFloat height = CGRectGetHeight(self.scanFrame)/ CGRectGetHeight(self.preview.frame);
output.rectOfInterest = CGRectMake(y, x, height, width);
// 創建環境光感輸出流
AVCaptureVideoDataOutput *lightOutput = [[AVCaptureVideoDataOutput alloc] init];
[lightOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
_session = [[AVCaptureSession alloc] init];
//高質量采集率
[_session setSessionPreset:AVCaptureSessionPresetHigh];
[_session addInput:input];
[_session addOutput:output];
[_session addOutput:lightOutput];
//設置掃碼支持的編碼格式(這里設置條形碼和二維碼兼容)
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeCode128Code];
}
return _session;
}
- 掃描出結果的代理方法
#pragma mark -- AVCaptureMetadataOutputObjectsDelegate
//掃描完成后執行
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if (metadataObjects.count > 0){
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
// 掃描完成后的字符
// NSLog(@"掃描出 %@",metadataObject.stringValue);
if(self.scanFinishedBlock != nil){
self.scanFinishedBlock(metadataObject.stringValue);
}
}
}
#pragma mark- AVCaptureVideoDataOutputSampleBufferDelegate的方法
//掃描過程中執行,主要用來判斷環境的黑暗程度
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
if (self.monitorLightBlock == nil) {
return;
}
CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
CFRelease(metadataDict);
NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
float brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
// NSLog(@"環境光感 : %f",brightnessValue);
// 根據brightnessValue的值來判斷是否需要打開和關閉閃光燈
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
BOOL result = [device hasTorch];// 判斷設備是否有閃光燈
if ((brightnessValue < 0) && result) {
// 環境太暗,可以打開閃光燈了
}else if((brightnessValue > 0) && result){
// 環境亮度可以
}
if (self.monitorLightBlock != nil) {
self.monitorLightBlock(brightnessValue);
}
}
三、用法
- 實例化WSLNativeScanTool工具類和WSLScanView界面類;用法很簡單,頭文件里注釋的也挺詳細的。
//輸出流視圖
UIView *preview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)];
[self.view addSubview:preview];
__weak typeof(self) weakSelf = self;
//構建掃描樣式視圖
_scanView = [[WSLScanView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)];
_scanView.scanRetangleRect = CGRectMake(60, 120, (self.view.frame.size.width - 2 * 60), (self.view.frame.size.width - 2 * 60));
_scanView.colorAngle = [UIColor greenColor];
_scanView.photoframeAngleW = 20;
_scanView.photoframeAngleH = 20;
_scanView.photoframeLineW = 2;
_scanView.isNeedShowRetangle = YES;
_scanView.colorRetangleLine = [UIColor whiteColor];
_scanView.notRecoginitonArea = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
_scanView.animationImage = [UIImage imageNamed:@"scanLine"];
_scanView.myQRCodeBlock = ^{
[WSLNativeScanTool createQRCodeImageWithString:@"https://www.jianshu.com/u/e15d1f644bea" andSize:CGSizeMake(250, 250) andBackColor:[UIColor whiteColor] andFrontColor:[UIColor orangeColor] andCenterImage:[UIImage imageNamed:@"piao"]];
createQRCodeController.qrString = @"https://www.jianshu.com/u/e15d1f644bea";
};
_scanView.flashSwitchBlock = ^(BOOL open) {
[weakSelf.scanTool openFlashSwitch:open];
};
[self.view addSubview:_scanView];
//初始化掃描工具
_scanTool = [[WSLNativeScanTool alloc] initWithPreview:preview andScanFrame:_scanView.scanRetangleRect];
_scanTool.scanFinishedBlock = ^(NSString *scanString) {
NSLog(@"掃描結果 %@",scanString);
[weakSelf.scanTool sessionStopRunning];
[weakSelf.scanTool openFlashSwitch:NO];
};
_scanTool.monitorLightBlock = ^(float brightness) {
NSLog(@"環境光感 : %f",brightness);
if (brightness < 0) {
// 環境太暗,顯示閃光燈開關按鈕
[weakSelf.scanView showFlashSwitch:YES];
}else if(brightness > 0){
// 環境亮度可以,且閃光燈處於關閉狀態時,隱藏閃光燈開關
if(!weakSelf.scanTool.flashOpen){
[weakSelf.scanView showFlashSwitch:NO];
}
}
};
[_scanTool sessionStartRunning];
[_scanView startScanAnimation];
四、項目結構
五、其他補充
暫沒
iOS 原生二維碼掃描和生成
注:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權