二維碼掃描
前言:
最近的項目中使用到了二維碼,二維碼這個模塊功能也完成;覺得還是有必要總結一下用來做記錄。好長時間沒有寫二維碼了都忘記在差不多了,重新拾起來還是挻快的。
二維碼使用場景:
生活中有很多的地方都在使用,比如把它分享給朋友,通過掃描來關注平台。它的功能有生成二維碼、掃描二維碼、從相冊中讀取二維碼。主要從這幾個方面來講二維碼的使用,最后會封裝一個方便快捷使用的庫,供想快速集成的使用者。
1.生成二維碼
效果圖:
1. 創建二維碼濾鏡--CIFilter
1.1 恢復濾鏡的默認屬性
1.2 設置濾鏡的輸入數據
- 將傳入的字符串轉換成NSData數據
- 通過KVC來設置輸入的內容
inputMessage
1.3 二維碼容錯率
- inputCorrectionLevel 是一個單字母(@"L", @"M", @"Q", @"H" 中的一個),表示不同級別的容錯率,默認為 @"M".
- QR碼有容錯能力,QR碼圖形如果有破損,仍然可以被機器讀取內容,最高可以到7%~30%面積破損仍可被讀取,相對而言,容錯率愈高,QR碼圖形面積愈大。所以一般折衷使用15%容錯能力。
- L水平 7%的字碼可被修正.
- M水平 15%的字碼可被修正
- Q水平 25%的字碼可被修正
- H水平 30%的字碼可被修正
代碼: [filter setValue:@"H" forKey:@"inputCorrectionLevel"];
1.4 獲取濾鏡輸出的圖片
1.5 將CIImage轉換成UIImage
1.6 通過位圖創建高清圖片
1.7 圖片合成
運行報錯:
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextSetInterpolationQuality: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGBitmapContextCreateImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextSetBlendMode: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextSetAlpha: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Jan 3 16:35:22 二維碼掃描demo[4032] <Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
找到原因:
通過打斷點的方式查到創建filter的時候,filter為空;是因為我在創建的時候使用了宏定義;
錯誤的方式:
//1.創建濾鏡
CIFilter *filter = [CIFilter filterWithName: CIFILETERNAME];
正確的方式:
備注:filter的名字只能是這個不能是別的:CIQRCodeGenerator
//1.創建濾鏡
CIFilter *filter = [CIFilter filterWithName: @"CIQRCodeGenerator"];
2.掃描二維碼
效果圖:
- 創建上下文
- 創建一個探測器
- 直接開始識別圖片,獲取圖片特征
CIImage *imageCI = [[CIImage alloc] initWithImage:self.sourceImage]; NSArray <CIFeature *> *features = [detector featuresInImage:imageCI];
NSMutableArray *resultArray = @[].mutableCopy;
for (CIFeature *feature in features) {
CIQRCodeFeature *tempFeature = (CIQRCodeFeature *)feature;
[resultArray addObject:tempFeature.messageString];
//獲取到二維碼的東西
self.urlString = tempFeature.messageString;
if (isDrawCodeFrame) {
tempImage = [self drawQRCodeFrameFeatre: tempFeature toImage: tempImage];
}
}
4. 讀取特征
3.生成二維碼
效果圖:
- 判斷輸入輸出能否加入當前會話
- 添加一個視頻預覽圖層
- 設置輸出數據媒體類型
- 添加預欄圖層,放在最底層
- 添加繪圖圖層到預欄圖層上面
- 開始掃描
代理
#pragma mark - AVCaptureMetadataOutputObjectsDelegate代理
//得到掃描結果
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
//1.移除之前的邊框
[self removeQRCodeFrame];
if (metadataObjects == nil || metadataObjects.count == 0) {
NSLog(@"未能識別");
}
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[metadataObjects.lastObject stringValue]]];
for (AVMetadataObject *obj in metadataObjects) {
//轉換成機器可讀的編碼數據
AVMetadataMachineReadableCodeObject *codeObj = (AVMetadataMachineReadableCodeObject *)[self.previerLayer transformedMetadataObjectForMetadataObject:obj];
//繪制二維碼邊框
[self drawQRBorderShape:codeObj];
}
}