iOS系統原生 二維碼的生成、掃描和讀取(高清、彩色)


  由於近期工作中遇到了個需求:需要將一些固定的字段 在多個移動端進行相互傳輸,所以就想到了 二維碼 這個神奇的東東! 現在的大街上、連個攤煎餅的大媽 都有自己的二維碼來讓大家進行掃碼支付。可見現在的二維碼使用率多高, 不光如此,在很多的社交類的APP 基本都有掃一掃加好友這個功能吧,因此決定學一學這個神奇的東西。

  查找了一些資料博客啊發現,iOS7之前 對於開發人員來說 熟悉的第三方QRCode庫有: 


    • ZXing

      Google出品並開源 一直到現在都還有專人維護 是世界上使用最廣的二維碼庫 iOS上比較穩定的移植版是ZXingObj

    • ZBar
      功能上與ZXing不相伯仲 可惜的是項目在2012年之后就不維護了 雖然代碼到現在還可以使用

但是iOS7之后呢,系統框架已經集成二維碼的生成與讀取, 這使開發變得方便很多, 並且會比第三方更加效率。今天就來講講用系統原生的方式 來實現二維碼的生成和掃描吧

 ( 一 )高清二維碼

系統二維碼主要通過  CIFilter  的對象來完成, 當然首先我們需要先導入這個類所在的框架,並實現下面的代碼

   #import <CoreImage/CoreImage.h>

      // 1.創建過濾器 -- 蘋果沒有將這個字符定義為常量
     CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
      
      // 2.過濾器恢復默認設置
      [filter setDefaults];
     
     // 3.給過濾器添加數據(正則表達式/帳號和密碼) -- 通過KVC設置過濾器,只能設置NSData類型
     NSString *dataString = @"http://www.baidu.com";
     NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
     [filter setValue:data forKeyPath:@"inputMessage"];
     
     // 4.獲取輸出的二維碼
     CIImage *outputImage = [filter outputImage];
     
     // 5.顯示二維碼
     UIImage *image = [UIImage imageWithCIImage:outputImage];

  


通過上面這種最簡單的方式 生成的二維碼很模糊,而且二維碼的大小也不方便控制,

對於我們來說,需求的是一張 能控制大小,並且高清顯示的二維碼,因此我們需要用一種方式 將CIImage 轉為我們心目中那個UIImage
/** 根據CIImage生成指定大小的UIImage */
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat)size {
    CGRect extent = CGRectIntegral(image.extent);
    CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
    
    // 1.創建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];
}

( 二 )彩色二維碼

在使用過程中發現了個問題, 就是當我們使用一個長度過長的字段 去生成高清二維碼的時候,這個二維碼 就會變得非常密集、濃稠, 用手機來掃描的時候,由於手機攝像頭像素問題很難讀取到這個二維碼,因此我需要一個 彩色二維碼來增加它的辨識度。

 

    // 1、創建濾鏡對象
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    
    // 恢復濾鏡的默認屬性
    [filter setDefaults];
    
    // 2、設置數據
    NSString *string_data = @"http://www.baidu.com";
    NSData *qrImageData = [string_data dataUsingEncoding:NSUTF8StringEncoding];
    
    // 設置過濾器的輸入值, KVC賦值
    [filter setValue:qrImageData forKey:@"inputMessage"];
    
    // 3、獲得濾鏡輸出的圖像
    CIImage *outputImage = [filter outputImage];
    
    // 圖片小於(27,27),我們需要放大
    outputImage = [outputImage imageByApplyingTransform:CGAffineTransformMakeScale(9, 9)];
    
    
    // 4、創建彩色過濾器(彩色的用的不多)
    CIFilter * color_filter = [CIFilter filterWithName:@"CIFalseColor"];
    
    // 設置默認值
    [color_filter setDefaults];
    
    // 5、KVC 給私有屬性賦值
    [color_filter setValue:outputImage forKey:@"inputImage"];
    
    // 6、需要使用 CIColor 為背景顏色 和 主顏色 上色
    // inputColor0:背景顏色 ,inputColor1 主顏色
    // 注意不要使用 [CIColor redColor][CIColor blueColor],這些類似於UIColor的方法只有在iOS 10系統才有
    [color_filter setValue:[CIColor colorWithRed:1 green:1 blue:1] forKey:@"inputColor0"];
    [color_filter setValue:[CIColor colorWithRed:0 green:0 blue:1] forKey:@"inputColor1"];
    
    // 7、設置輸出
    CIImage *colorImage = [color_filter outputImage];

    //8、輸出UIImage
    UIImage *image = [UIImage imageWithCIIimage:colorImage];

 

  

掃描二維碼

 掃描主要使用的是AVFoundation 使用起來也非常的簡單 ,通過設置<AVCaptureMetadataOutputObjectsDelegate>代理可以監聽掃描到的二維碼中的信息

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController () <AVCaptureMetadataOutputObjectsDelegate>
/// 會話對象
@property (nonatomic, strong) AVCaptureSession *session;
/// 圖層類
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 // 1、獲取攝像設備
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // 2、創建輸入流
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    
    // 3、創建輸出流
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    
    // 4、設置代理 在主線程里刷新
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // 設置掃描范圍(每一個取值0~1,以屏幕右上角為坐標原點)
    // 注:微信二維碼的掃描范圍是整個屏幕,這里並沒有做處理(可不用設置)
    output.rectOfInterest = CGRectMake(0.05, 0.2, 0.7, 0.6);
    
    // 5、初始化鏈接對象(會話對象)
    self.session = [[AVCaptureSession alloc] init];
    // 高質量采集率
    [_session setSessionPreset:AVCaptureSessionPresetHigh];
    
    // 5.1 添加會話輸入
    [_session addInput:input];
    
    // 5.2 添加會話輸出
    [_session addOutput:output];
    
    // 6、設置輸出數據類型,需要將元數據輸出添加到會話后,才能指定元數據類型,否則會報錯
    // 設置掃碼支持的編碼格式(如下設置條形碼和二維碼兼容)
    output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code,  AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
    
    // 7、實例化預覽圖層, 傳遞_session是為了告訴圖層將來顯示什么內容
    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    _previewLayer.frame = self.view.layer.bounds;
    
    // 8、將圖層插入當前視圖
    [self.view.layer insertSublayer:_previewLayer atIndex:0];
    
    // 9、啟動會話
    [_session startRunning];
}

#pragma mark - 獲取掃描結果
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects.count > 0) {
        AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
        NSLog(@"%@", object.stringValue);
    }
}

@end

 AVCaptureMetadataOutput 有個屬性 rectOfInterest 他是用來控制你屏幕掃描的范圍的,默認是按照整個屏幕來掃描,rectOfInterest的值的范圍都是0-1 是按比例取值而不是實際尺寸 不過其實也很簡單 只要換算一下就好了 ,這里唯一要注意的一點是 rectOfInterest 都是按照橫屏來計算的 所以當豎屏的情況下 x軸和y軸要交換一下

 

讀取二維碼 

讀取主要用到CoreImage 不過要強調的是讀取二維碼的功能只有在iOS8之后才支持,讀取的代碼也非常的簡單

//首先拿到  我們需要讀取的那個圖片
UIImage * srcImage = qrcodeImage;

CIContext *context = [CIContext contextWithOptions:nil];

 // CIDetector(CIDetector可用於人臉識別)進行圖片解析,聲明一個CIDetector,並設定識別類型 CIDetectorTypeQRCode
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

CIImage *image = [CIImage imageWithCGImage:srcImage.CGImage];

    // 取得識別結果是個數組
    NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
    for (int index = 0; index < [features count]; index ++) {
        CIQRCodeFeature *feature = [features objectAtIndex:index];
        //這個String就是我們從二維碼中獲取到的信息
        NSString *scannedResult = feature.messageString;
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM