今天有時(shí)間整理下原生的二維碼的生成與讀取。
準(zhǔn)備工作
- 導(dǎo)入
<CoreImage/CoreImage.h>,生成二維碼用 - 導(dǎo)入
<AVFoundation/AVFoundation.h>,讀取二維碼用 - 協(xié)議
AVCaptureMetadataOutputObjectsDelegate,這是有關(guān)攝像設(shè)備輸出的相關(guān)代理 -
注意:掃描二維碼的時(shí)候,要在info.plist文件中添加字段,否則會(huì)崩潰(iOS10之后的隱私權(quán)限問(wèn)題)
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能訪問(wèn)相冊(cè)</string>
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能訪問(wèn)相機(jī)</string>
二維碼的生成
1.生成二維碼的步驟:
1)導(dǎo)入CoreImage框架
2)通過(guò)濾鏡CIFilter生成二維碼
代碼如下
調(diào)用下方根據(jù)字符串生成二維碼的方法即可獲得二維碼
// 生成二維碼
- (UIImage *)createImageWithString:(NSString *)string{
// 1.實(shí)例化二維碼濾鏡
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// 2.恢復(fù)濾鏡的默認(rèn)屬性(因?yàn)闉V鏡可能保存上一次的屬性)
[filter setDefaults];
// 3.講字符串轉(zhuǎn)換為NSData
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];*
// 4.通過(guò)KVO設(shè)置濾鏡inputMessage數(shù)據(jù)
[filter setValue:data forKey:@"inputMessage"];
// 5.通過(guò)了濾鏡輸出的圖像
CIImage *outputImage = [filter outputImage];
// 6.因?yàn)樯傻亩S碼模糊,所以通過(guò)createNonInterpolatedUIImageFormCIImage:outputImage來(lái)獲得高清的二維碼圖片
UIImage *image = [self getErWeiMaImageFormCIImage:outputImage withSize:200];
return image;
}
// 獲取高清二維碼圖片
- (UIImage *)getErWeiMaImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 1.創(chuàng)建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到圖片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
運(yùn)行結(jié)果
[圖片上傳失敗...(image-bbce1-1542166399362)]
????????????我是可愛(ài)的分割線????????????
二維碼的生成還是比較簡(jiǎn)單的,下面讓我們來(lái)看下二維碼的掃描
????????????我是可愛(ài)的分割線????????????
二維碼的讀取
1.讀取二維碼的步驟:
1)讀取二維碼需要導(dǎo)入AVFoundation框架(上方準(zhǔn)備工作的時(shí)候已經(jīng)說(shuō)了)
2)利用相機(jī)識(shí)別二維碼中的內(nèi)容(只能是真機(jī))
3)會(huì)話將相機(jī)采集到的二維碼圖像轉(zhuǎn)換成字符串?dāng)?shù)據(jù)
2.原生掃描中用到的幾個(gè)類
AVCaptureDevice // 拍攝設(shè)備
AVCaptureDeviceInput // 輸入設(shè)備
AVCaptureMetadataOutput // 元數(shù)據(jù)輸出
AVCaptureSession // 拍攝會(huì)話
AVCaptureVideoPreviewLayer // 視頻預(yù)覽圖層
代碼如下
- (void)readQRCode{
// 1.實(shí)例化拍攝裝備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 2.設(shè)置輸入設(shè)備
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
// 3.設(shè)置元數(shù)據(jù)輸出
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; // 設(shè)置代理
// 4.添加拍攝會(huì)話
self.session = [[AVCaptureSession alloc] init];
[self.session addInput:input]; // 添加會(huì)話輸入
[self.session addOutput:output]; // 添加會(huì)話輸出
[output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; // 設(shè)置輸出數(shù)據(jù)類型(需要將元數(shù)據(jù)輸出添加到會(huì)話后才能制定元數(shù)據(jù)類型,否則會(huì)報(bào)錯(cuò))
// 5.視頻預(yù)覽圖層
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session]; // 傳遞session是為了告訴圖層將來(lái)顯示什么內(nèi)容
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; // 顯示方式
// 設(shè)置videoGravity,顧名思義就是視頻播放時(shí)的拉伸方式,默認(rèn)是AVLayerVideoGravityResizeAspect
// AVLayerVideoGravityResizeAspect 保持視頻的寬高比并使播放內(nèi)容自動(dòng)適應(yīng)播放窗口的大小。
// AVLayerVideoGravityResizeAspectFill 和前者類似,但它是以播放內(nèi)容填充而不是適應(yīng)播放窗口的大小。最后一個(gè)值會(huì)拉伸播放內(nèi)容以適應(yīng)播放窗口.
// 因?yàn)榭紤]到全屏顯示以及設(shè)備自適應(yīng),這里我們采用fill填充
self.previewLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:self.previewLayer atIndex:0]; // 將圖層插入當(dāng)前圖層
// 6.啟動(dòng)會(huì)話
[self.session startRunning];
}
AVCaptureMetadataOutputObjectsDelegate 的代理方法
/**
掃描結(jié)果處理
@param captureOutput 輸出數(shù)據(jù)源
@param metadataObjects 掃描結(jié)果數(shù)組
@param connection 用于協(xié)調(diào)輸入與輸出之間的數(shù)據(jù)流
*/
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
// 1.判斷掃描結(jié)果的數(shù)據(jù)是否存在
if ([metadataObjects count] > 0) {
// 2.如果存在數(shù)據(jù),則停止會(huì)話
[self.session stopRunning];
// 3.刪除預(yù)覽圖層
[self.previewLayer removeFromSuperlayer];
AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects[0];
// AVMetadataMachineReadableCodeObject 是AVMetadataObject的具體子類定義的特性檢測(cè)一維或二維條形碼。
// AVMetadataMachineReadableCodeObject代表一個(gè)單一的照片中發(fā)現(xiàn)機(jī)器可讀的代碼。這是一個(gè)不可變對(duì)象描述條碼的特性和載荷。
// 在支持的平臺(tái)上,AVCaptureMetadataOutput輸出檢測(cè)機(jī)器可讀的代碼對(duì)象的數(shù)組
NSString *stringValue = metadataObject.stringValue;
if ([stringValue containsString:@"http"]) {
// 如果是字符串,則打開(kāi)連接
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringValue] options:[NSDictionary dictionary] completionHandler:^(BOOL success) {
if (success) {
NSLog(@"成功");
}
}];
}else{
NSLog(@"普通字符串:%@",stringValue); // 可以將字符串放到需要用到的地方(比如label)
}
}
}