iOS系统原生二维码扫描与生成

iOS系统原生二维码代码

1.扫描识别各种类型的码 (条形码, 二维码 , 彩色码 等)

2.添加扫描视图的范围(中间框框)

3.开启和关闭闪光灯

4.识别相册中的二维码图片

5.生成二维码图片

 

先展示下效果图:

iOS系统原生二维码扫描与生成_第1张图片

iOS系统原生二维码扫描与生成_第2张图片

 

1. 二维码扫描识别

1.首先导入库文件

#import

2.接着签订需要的代理, 并创建所需要的属性(这里我只讲述关键代码)

AVCaptureMetadataOutputObjectsDelegate : 用于扫描获取到数据后的回调 , (metadataObjects: 扫描二维码数据信息)

#import

#import "QRScanView.h"

// 二维码扫描器

@interface QrCodeReaderViewController ()

    UIImagePickerControllerDelegate, UINavigationControllerDelegate>

@property (nonatomic, strong) AVCaptureSession *session;

@property (nonatomic, strong) AVCaptureVideoPreviewLayer *layer;

@property (nonatomic, strong) AVCaptureMetadataOutput *output;

@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;

@property (nonatomic, assign) CGRect scanRect;

@property (nonatomic, weak) QRScanView *scanView;

@end

@implementation QrCodeReaderViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    CGFloat scanWH = 220;

    CGFloat scanX = (kScreenWidth - scanWH) * 0.5;

    CGFloat scanY = (kScreenHeight - scanWH) * 0.5;

    self.scanRect = CGRectMake(scanX, scanY, scanWH, scanWH);

    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

    if (status == AVAuthorizationStatusAuthorized || status == AVAuthorizationStatusRestricted) {

        [self loadScanView];

    } else if (status == AVAuthorizationStatusNotDetermined) {

        // 请求使用相机权限

        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {

            if (granted) {

                dispatch_async(dispatch_get_main_queue(), ^{

                    [self loadScanView];

                });

            } else {

                [[[UIAlertView alloc] initWithTitle:@"无权限访问相机" message:@"无权限访问相机" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];

            }

        }];

    } else {

        [[[UIAlertView alloc] initWithTitle:@"无权限访问相机" message:@"无权限访问相机" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];

    }

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"相册" style:UIBarButtonItemStylePlain target:self action:@selector(analyzeOnClick)];

}

#pragma mark - 从相册解析二维码图片

- (void)analyzeOnClick {

    UIImagePickerController *picker = [[UIImagePickerController alloc] init];

    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

    picker.delegate = self;

    [self presentViewController:picker animated:YES completion:nil];

}

- (void)loadScanView {

    //获取摄像设备

    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    //创建设备输入流

    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];

    //创建元数据输出流

    self.output = [[AVCaptureMetadataOutput alloc]init];

    //为输出流对象设置代理 并在主线程里刷新

    [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

    //初始化链接对象

    self.session = [[AVCaptureSession alloc]init];

    //设置高质量采集率

    [self.session setSessionPreset:AVCaptureSessionPresetHigh];

    // 添加设备输入流

    [self.session addInput:input];

    // 添加设备输出流

    [self.session addOutput:self.output];

    // 创建摄像数据输出流并将其添加到会话对象上 --> 用于识别光线强弱

    self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];

    [self.videoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

    [self.session addOutput:self.videoDataOutput];

    //设置扫码支持的编码格式(如下设置条形码和二维码兼容)

    self.output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,//二维码

                                //以下为条形码,如果项目只需要扫描二维码,下面都不要写

                                AVMetadataObjectTypeEAN13Code,

                                AVMetadataObjectTypeEAN8Code,

                                AVMetadataObjectTypeUPCECode,

                                AVMetadataObjectTypeCode39Code,

                                AVMetadataObjectTypeCode39Mod43Code,

                                AVMetadataObjectTypeCode93Code,

                                AVMetadataObjectTypeCode128Code,

                                AVMetadataObjectTypePDF417Code];

    // 实例化预览图层, 用于显示会话对象

    self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];

    // 保持纵横比;填充层边界

    self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill;

//    layer.frame = self.view.layer.bounds;

    self.layer.frame = [UIScreen mainScreen].bounds;

    [self.view.layer insertSublayer:self.layer atIndex:0];

    // 在block中使用weak self,不然会导致该controller的内存无法回收

    __weak typeof(self) wekself = self;

    // 添加通知

    [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {

        // 如果不设置,整个屏幕都可以扫

        wekself.output.rectOfInterest = [wekself.layer metadataOutputRectOfInterestForRect:wekself.scanRect];

    }];

    // 添加扫描视图

    QRScanView *scanView = [[QRScanView alloc] initWithScanRect:self.scanRect];

    [self.view addSubview:scanView];

    self.scanView = scanView;

    scanView.offFlashBlock = ^(BOOL flag) {

        [wekself offFlashWithMode:(flag ? AVCaptureTorchModeOn : AVCaptureTorchModeOff)];

    };

    //开始捕获

    [self.session startRunning];

}

// 根据Mode是否打开闪光灯

- (void)offFlashWithMode:(AVCaptureTorchMode)mode {

    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;

    if ([captureDevice hasTorch]) {

        BOOL locked = [captureDevice lockForConfiguration:&error];

        if (locked && error == nil) {

            // 打开手电筒 | 关闭手电筒

            captureDevice.torchMode = mode;

            [captureDevice unlockForConfiguration];

        }

    }

}

#pragma mark - AVCaptureMetadataOutputObjectsDelegate

// 扫描到数据后的回调

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{

    if (metadataObjects.count>0) {

        [self.session stopRunning];

        AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects[0];

        if (self.qrcodeValueBlock) {

            self.qrcodeValueBlock(metadataObject.stringValue);

        }

//        [self.navigationController popViewControllerAnimated:YES];

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            [self.session startRunning];

        });

    }

}

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate

// 获取到光线的强弱值

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

    // 这个方法会时时调用,但内存很稳定

    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);

    self.scanView.brightnessValue = brightnessValue;

}

#pragma mark - UIImagePickerContorllerDelegate

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    // 对选取照片的处理,如果选取的图片尺寸过大,则压缩选取图片,否则不处理

    UIImage *originalImage = info[UIImagePickerControllerOriginalImage];

    UIImage *image = [self imageSizeWithScreenImage:originalImage];

    // CIDetector (CIDetector可用于人脸识别)进行图片解析,从而使我们可以便捷的从相册中获取到二维码

    // 声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode

    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

    // 获取识别结果

    NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];

    if ([features count] <= 0) {

        [BaseViewController alertWithTitle:@"识别结果为nil"];

    } else {

        CIQRCodeFeature *feature = [features firstObject];

        [BaseViewController alertWithTitle:feature.messageString];

//        for (CIQRCodeFeature *feature in features) {

//            NSLog(@"%@", feature.messageString);

//        }

    }

    [picker dismissViewControllerAnimated:YES completion:nil];

}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

    [picker dismissViewControllerAnimated:YES completion:nil];

}

// 返回一张不超过屏幕尺寸的image

- (UIImage *)imageSizeWithScreenImage:(UIImage *)image {

    CGFloat imageW = image.size.width;

    CGFloat imageH = image.size.height;

    CGFloat screenW = kScreenWidth;

    CGFloat screenH = kScreenHeight;

    if (imageW <= screenW && imageH <= screenH) {

        return image;

    }

    CGFloat max = MAX(imageW, imageH);

    CGFloat scale = max / (screenH * 2.0);

    CGSize size = CGSizeMake(imageW / scale, imageW / scale);

    UIGraphicsBeginImageContext(size);

    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;

}

- (void)dealloc {

    NSLog(@"QrCodeReader - dealloc");

    // 将引用置空

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    [self.session stopRunning];

    self.session = nil;

    self.layer = nil;

    self.output = nil;

    self.videoDataOutput = nil;

}

@end

 

// 自定义扫描视图

@interface QRScanView : UIView

- (instancetype)initWithScanRect:(CGRect)scanRect;

// 是否隐藏开启闪光灯按钮

@property (nonatomic, assign) float brightnessValue;

@property (nonatomic, copy) void (^offFlashBlock)(BOOL flag);

@end

#import "QRScanView.h"

@interface QRScanView()

@property (nonatomic, weak) UIView *lineView;

@property (nonatomic, weak) UIButton *flashBtn;

@end

@implementation QRScanView {

    CGRect _scanRect;

    dispatch_source_t timer;

}

- (instancetype)initWithScanRect:(CGRect)scanRect {

    if (self = [super initWithFrame:[UIScreen mainScreen].bounds]) {

        self.backgroundColor = [UIColor clearColor];

        _scanRect = scanRect;

        UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(scanRect.origin.x, scanRect.origin.y, scanRect.size.width, 1)];

        lineView.backgroundColor = [UIColor greenColor];

        lineView.tag = 1;

        [self addSubview:lineView];

        self.lineView = lineView;

        UIButton *flashBtn = [[UIButton alloc] initWithFrame:CGRectMake(scanRect.origin.x, CGRectGetMaxY(scanRect) + 15, scanRect.size.width, 40)];

        flashBtn.hidden = YES;

        [flashBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];

        [flashBtn setTitle:@"开启闪光灯" forState:UIControlStateNormal];

        [flashBtn addTarget:self action:@selector(openFlashOnClick) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:flashBtn];

        self.flashBtn = flashBtn;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            [self dispatchScanViewAnimation];

        });

    }

    return self;

}

- (void)openFlashOnClick {

    self.flashBtn.selected = !self.flashBtn.selected;

    [self.flashBtn setTitle:(self.flashBtn.selected ? @"关闭闪光灯" : @"开启闪光灯") forState:UIControlStateNormal];

    if (self.offFlashBlock) {

        self.offFlashBlock(self.flashBtn.selected);

    }

}

- (void)setBrightnessValue:(float)brightnessValue {

    _brightnessValue = brightnessValue;

    if (brightnessValue < -1) {

        self.flashBtn.hidden = NO;

    } else if (self.flashBtn.selected == NO && self.flashBtn.hidden == NO) {

        self.flashBtn.hidden = YES;

    }

}

- (void)dispatchScanViewAnimation {

    if (timer) {

        dispatch_source_cancel(timer);

        timer = nil;

    }

    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(1, 1));

    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.003 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    dispatch_source_set_event_handler(timer, ^{

        dispatch_async(dispatch_get_main_queue(), ^{

            CGRect frame = self.lineView.frame;

            if (self.lineView.tag == 2) {

                frame.origin.y -= 0.5;

                if (frame.origin.y <= _scanRect.origin.y) {

                    self.lineView.tag = 1;

                }

            } else {

                frame.origin.y += 0.5;

                if (CGRectGetMaxY(frame) >= CGRectGetMaxY(_scanRect)) {

                    self.lineView.tag = 2;

                }

            }

            self.lineView.frame = frame;

        });

    });

    dispatch_resume(timer);

}

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    [[[UIColor blackColor] colorWithAlphaComponent:0.5] setFill];

    CGMutablePathRef screenPath = CGPathCreateMutable();

    CGPathAddRect(screenPath, NULL, self.bounds);

    CGMutablePathRef scanPath = CGPathCreateMutable();

    CGPathAddRect(scanPath, NULL, _scanRect);

    CGMutablePathRef path = CGPathCreateMutable();

    CGPathAddPath(path, NULL, screenPath);

    CGPathAddPath(path, NULL, scanPath);

    CGContextAddPath(ctx, path);

    /** kCGPathEOFill:奇偶规则填充(被覆盖过奇数点的填充,被覆盖过偶数点的不填充)

    就比如说从任意位置p作一条射线,若与该射线相交的多边形边的数目为奇数,则p是在多边形内,

    就去填充,否则就不填充。*/

    CGContextDrawPath(ctx, kCGPathEOFill);

    CGPathRelease(path);

    CGPathRelease(screenPath);

    CGPathRelease(scanPath);

}

@end

2.二维码扫描范围设置

1.AVCaptureMetadataOutput  扫描数据的输出对象

2.AVCaptureVideoPreviewLayer 扫描视图

3.rectOfInterest  该属性就是设置扫描内容的范围大小,需要使用[layer metadataOutputRectOfInterestForRect:_scanRect]; 方法进行矩形转换获取扫描的范围大小

// 只有添加该通知,在该通知里面设置rectOfInterest属性才有效

    [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {

        // 如果不设置,整个屏幕都可以扫

        output.rectOfInterest = [layer metadataOutputRectOfInterestForRect:_scanRect];

    }];

3.开启和关闭闪光灯

// 创建摄像数据输出流并将其添加到会话对象上 --> 用于识别光线强弱

    self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];

    [self.videoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

    [self.session addOutput:self.videoDataOutput];

// 根据Mode是否打开闪光灯

- (void)offFlashWithMode:(AVCaptureTorchMode)mode {

    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;

    if ([captureDevice hasTorch]) {

        BOOL locked = [captureDevice lockForConfiguration:&error];

        if (locked && error == nil) {

            // 打开手电筒 | 关闭手电筒

            captureDevice.torchMode = mode;

            [captureDevice unlockForConfiguration];

        }

    }

}

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate

// 获取到光线的强弱值

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

    // 这个方法会时时调用,但内存很稳定

    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);

    self.scanView.brightnessValue = brightnessValue;

}

4.识别相册中的二维码图片

实现两个代理: UIImagePickerControllerDelegate, UINavigationControllerDelegate

#pragma mark - 从相册解析二维码图片

- (void)analyzeOnClick {

    UIImagePickerController *picker = [[UIImagePickerController alloc] init];

    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

    picker.delegate = self;

    [self presentViewController:picker animated:YES completion:nil];

}

#pragma mark - UIImagePickerContorllerDelegate

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    // 对选取照片的处理,如果选取的图片尺寸过大,则压缩选取图片,否则不处理

    UIImage *originalImage = info[UIImagePickerControllerOriginalImage];

    UIImage *image = [self imageSizeWithScreenImage:originalImage];

    // CIDetector (CIDetector可用于人脸识别)进行图片解析,从而使我们可以便捷的从相册中获取到二维码

    // 声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode

    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

    // 获取识别结果

    NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];

    if ([features count] <= 0) {

        [BaseViewController alertWithTitle:@"识别结果为nil"];

    } else {

        CIQRCodeFeature *feature = [features firstObject];

        [BaseViewController alertWithTitle:feature.messageString];

//        for (CIQRCodeFeature *feature in features) {

//            NSLog(@"%@", feature.messageString);

//        }

    }

    [picker dismissViewControllerAnimated:YES completion:nil];

}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

    [picker dismissViewControllerAnimated:YES completion:nil];

}

// 返回一张不超过屏幕尺寸的image

- (UIImage *)imageSizeWithScreenImage:(UIImage *)image {

    CGFloat imageW = image.size.width;

    CGFloat imageH = image.size.height;

    CGFloat screenW = kScreenWidth;

    CGFloat screenH = kScreenHeight;

    if (imageW <= screenW && imageH <= screenH) {

        return image;

    }

    CGFloat max = MAX(imageW, imageH);

    CGFloat scale = max / (screenH * 2.0);

    CGSize size = CGSizeMake(imageW / scale, imageW / scale);

    UIGraphicsBeginImageContext(size);

    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;

}

5.二维码图片生成

1.需要包含头文件

#import 

// 二维码生成器

@interface QrCodeGeneratorViewController ()

@property (weak, nonatomic) IBOutlet UITextView *contentTxv;

@property (weak, nonatomic) IBOutlet UIImageView *qrcodeImv;

- (IBAction)generatorOnClick;

@end

@implementation QrCodeGeneratorViewController

- (IBAction)generatorOnClick {

    // 0.导入头文件 #import

    // 1.创建过滤器 -- 苹果没有将这个字符封装成常量

    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];

    // 2.过滤器恢复默认设置

    [filter setDefaults];

    // 3.给过滤器添加数据(正则表达式/帐号和密码) -- 通过KVC设置过滤器,只能设置NSData类型

    NSString *dataString = self.contentTxv.text;

    NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];

    [filter setValue:data forKeyPath:@"inputMessage"];

    // 4.获取输出的二维码

    CIImage *outputImage = [filter outputImage];

    // 5.显示二维码

    //    self.qrcodeImv.image = [UIImage imageWithCIImage:outputImage];

    // 显示放大后清晰的二维码图片

    self.qrcodeImv.image = [self createNonInterpolatedUIImageFormCIImage:outputImage withSize:120];

}

/**

*  根据CIImage生成指定大小的UIImage

*

*  @param image CIImage

*  @param size  图片宽度

*/

- (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];

}

@end

 


 

你可能感兴趣的:(功能模块,一天一读)