二维码扫描
在开发过程中,我们经常会遇到二维码扫描功能,下面是iOS原生扫描二维码的实现
类名:ScanViewController
.h 文件
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface ScanViewController : UIViewController<AVCaptureMetadataOutputObjectsDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (strong, nonatomic) AVCaptureDevice * device; //捕获设备,默认后置摄像头
@property (strong, nonatomic) AVCaptureDeviceInput * input; //输入设备
@property (strong, nonatomic) AVCaptureMetadataOutput * output;//输出设备,需要指定他的输出类型及扫描范围
@property (strong, nonatomic) AVCaptureSession * session; //AVFoundation框架捕获类的中心枢纽,协调输入输出设备以获得数据
@property (strong, nonatomic) AVCaptureVideoPreviewLayer * preview;//展示捕获图像的图层,是CALayer的子类
@property (strong, nonatomic) UIPinchGestureRecognizer *pinchGes;//缩放手势
//扫描框
@property (nonatomic,strong)UIImageView *imageView;
//扫描线
@property (nonatomic,retain)UIImageView *line;
@end
.m
#import "ScanViewController.h"
#import "CommunityHostUserMessageVC.h"
#define Height [UIScreen mainScreen].bounds.size.height
#define Width [UIScreen mainScreen].bounds.size.width
#define XCenter self.view.center.x
#define YCenter self.view.center.y
#define SHeight 20
#define SWidth (XCenter+30)
@interface ScanViewController ()
{
NSTimer *_timer;
int num;
BOOL upOrDown;
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *height;
@end
@implementation ScanViewController
#pragma mark ===========懒加载===========
//device
- (AVCaptureDevice *)device
{
if (_device == nil) {
//AVMediaTypeVideo是打开相机
//AVMediaTypeAudio是打开麦克风
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return _device;
}
//input
- (AVCaptureDeviceInput *)input
{
if (_input == nil) {
_input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
}
return _input;
}
//output --- output如果不打开就无法输出扫描得到的信息
// 设置输出对象解析数据时感兴趣的范围
// 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)
// 通过对这个值的观察, 我们发现传入的是比例
// 注意: 参照是以横屏的左上角作为, 而不是以竖屏
// out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
- (AVCaptureMetadataOutput *)output
{
if (_output == nil) {
_output = [[AVCaptureMetadataOutput alloc]init];
[_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//限制扫描区域(上下左右)
[_output setRectOfInterest:[self rectOfInterestByScanViewRect:_imageView.frame]];
}
return _output;
}
- (CGRect)rectOfInterestByScanViewRect:(CGRect)rect {
CGFloat width = CGRectGetWidth(self.view.frame);
CGFloat height = CGRectGetHeight(self.view.frame);
CGFloat x = (height - CGRectGetHeight(rect)) / 2 / height;
CGFloat y = (width - CGRectGetWidth(rect)) / 2 / width;
CGFloat w = CGRectGetHeight(rect) / height;
CGFloat h = CGRectGetWidth(rect) / width;
return CGRectMake(x, y, w, h);
}
//session
- (AVCaptureSession *)session
{
if (_session == nil) {
//session
_session = [[AVCaptureSession alloc]init];
[_session setSessionPreset:AVCaptureSessionPresetHigh];
if ([_session canAddInput:self.input]) {
[_session addInput:self.input];
}
if ([_session canAddOutput:self.output]) {
[_session addOutput:self.output];
}
}
return _session;
}
//preview
- (AVCaptureVideoPreviewLayer *)preview
{
if (_preview == nil) {
_preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
}
return _preview;
}
- (void)viewWillDisappear:(BOOL)animated{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
#pragma mark ==========ViewDidLoad==========
- (void)viewDidLoad{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
//1 判断是否存在相机
if (self.device == nil) {
[self showAlertViewWithMessage:@"未检测到相机"];
return;
}
self.view.backgroundColor = [UIColor whiteColor];
//打开定时器,开始扫描
[self addTimer];
//界面初始化
[self interfaceSetup];
//初始化扫描
[self scanSetup];
}
#pragma mark ==========初始化工作在这里==========
- (void)viewDidDisappear:(BOOL)animated
{
//视图退出,关闭扫描
[self.session stopRunning];
//关闭定时器
[_timer setFireDate:[NSDate distantFuture]];
}
//界面初始化
- (void)interfaceSetup
{
//1 添加扫描框
[self addImageView];
//添加模糊效果
// [self setOverView];
//添加开始扫描按钮
// [self addStartButton];
}
//添加扫描框
- (void)addImageView
{
_imageView = [[UIImageView alloc]initWithFrame:CGRectMake((Width-SWidth)/2, (Height-SWidth)/2, SWidth, SWidth)];
//显示扫描框
_imageView.image = [UIImage imageNamed:@"scanscanBg.png"];
[self.view addSubview:_imageView];
_line = [[UIImageView alloc]initWithFrame:CGRectMake(CGRectGetMinX(_imageView.frame)+5, CGRectGetMinY(_imageView.frame)+5, CGRectGetWidth(_imageView.frame), 3)];
_line.image = [UIImage imageNamed:@"scanLine"];
[self.view addSubview:_line];
UILabel * lable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
[lable setText:@"将二维码放入框内,自动识别"];
lable.textAlignment = NSTextAlignmentCenter;
lable.textColor = [UIColor whiteColor];
lable.font = [UIFont systemFontOfSize:14];
lable.center = CGPointMake(_imageView.center.x , _imageView.center.y+ SWidth/2 + 30);
[self.view addSubview:lable];
}
//初始化扫描配置
- (void)scanSetup
{
_height.constant = SafeAreaTopHeight;
//2 添加预览图层
self.preview.frame = CGRectMake(0, SafeAreaTopHeight, SCREEN_WIDTH, SCREEN_HEIGHT - SafeAreaTopHeight);
self.preview.videoGravity = AVLayerVideoGravityResize;
[self.view.layer insertSublayer:self.preview atIndex:0];
//3 设置输出能够解析的数据类型
//注意:设置数据类型一定要在输出对象添加到回话之后才能设置
[self.output setMetadataObjectTypes:@[AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode]];
//高质量采集率
[self.session setSessionPreset:AVCaptureSessionPresetHigh];
//4 开始扫描
[self.session startRunning];
}
//提示框alert
- (void)showAlertViewWithMessage:(NSString *)message{
//弹出提示框后,关闭扫描
[self.session stopRunning];
//弹出alert,关闭定时器
[_timer setFireDate:[NSDate distantFuture]];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"扫描结果" message:[NSString stringWithFormat:@"%@",message] preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
//点击alert,开始扫描
[self.session startRunning];
//开启定时器
[_timer setFireDate:[NSDate distantPast]];
}]];
[self presentViewController:alert animated:true completion:^{ }];
}
//打开系统相册
- (IBAction)chooseButtonClick:(UIButton*)button{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
//关闭扫描
[self stopScan];
//1 弹出系统相册
UIImagePickerController *pickVC = [[UIImagePickerController alloc]init];
//2 设置照片来源
/**
UIImagePickerControllerSourceTypePhotoLibrary,相册
UIImagePickerControllerSourceTypeCamera,相机
UIImagePickerControllerSourceTypeSavedPhotosAlbum,照片库
*/
pickVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
//3 设置代理
pickVC.navigationBar.translucent = NO; //去除毛玻璃效果
pickVC.delegate = self;
//4.随便给他一个转场动画
self.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
[self presentViewController:pickVC animated:YES completion:nil];
} else {
[self showAlertViewWithTitle:@"打开失败" withMessage:@"相册打开失败。设备不支持访问相册,请在设置->隐私->照片中进行设置!"];
}
}
//从相册中选取照片&读取相册二维码信息
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
//1 获取选择的图片
UIImage *image = info[UIImagePickerControllerOriginalImage];
//初始化一个监听器
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
[picker dismissViewControllerAnimated:YES completion:^{
//监测到的结果数组
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
if (features.count >= 1) {
//结果对象
CIQRCodeFeature *feature = [features objectAtIndex:0];
NSString *scannedResult = feature.messageString;
#pragma mark 扫描结果
} else {
[self showAlertViewWithTitle:NSLocalizedString(@"alert_title", @"提示") withMessage:NSLocalizedString(@"QRcode_reading_failed", @"二维码读取失败")];
}
}];
}
- (IBAction)backAction:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark ===========重启扫描&闪光灯===========
//添加开始扫描按钮
- (void)addStartButton{
UIButton *startButton = [UIButton buttonWithType:UIButtonTypeCustom];
startButton.frame = CGRectMake(60, 420, 80, 40);
startButton.backgroundColor = [UIColor orangeColor];
[startButton addTarget:self action:@selector(startButtonClick) forControlEvents:UIControlEventTouchUpInside];
[startButton setTitle:@"扫描" forState:UIControlStateNormal];
[self.view addSubview:startButton];
}
- (void)startButtonClick{
//清除imageView上的图片
self.imageView.image = [UIImage imageNamed:@""];
//开始扫描
[self starScan];
}
- (void)systemLightSwitch:(BOOL)open{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device hasTorch]) {
[device lockForConfiguration:nil];
if (open) {
[device setTorchMode:AVCaptureTorchModeOn];
} else {
[device setTorchMode:AVCaptureTorchModeOff];
}
[device unlockForConfiguration];
}
}
#pragma mark ===========添加提示框===========
//提示框alert
- (void)showAlertViewWithTitle:(NSString *)aTitle withMessage:(NSString *)aMessage{
//弹出提示框后,关闭扫描
[self.session stopRunning];
//弹出alert,关闭定时器
[_timer setFireDate:[NSDate distantFuture]];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:aTitle message:[NSString stringWithFormat:@"%@",aMessage] preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Sure", @"确定") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
//点击alert,开始扫描
[self.session startRunning];
//开启定时器
[_timer setFireDate:[NSDate distantPast]];
}]];
[self presentViewController:alert animated:true completion:^{}];
}
#pragma mark ===========扫描的代理方法===========
//得到扫描结果
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
if ([metadataObjects count] > 0) {
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0];
if ([metadataObject isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
NSString *stringValue = [metadataObject stringValue];
if (stringValue != nil) {
[self.session stopRunning];
#pragma mark 扫描结果
}
}
}
}
#pragma mark ============添加模糊效果============
- (void)setOverView {
CGFloat width = CGRectGetWidth(self.view.frame);
CGFloat height = CGRectGetHeight(self.view.frame);
CGFloat x = CGRectGetMinX(_imageView.frame);
CGFloat y = CGRectGetMinY(_imageView.frame);
CGFloat w = CGRectGetWidth(_imageView.frame);
CGFloat h = CGRectGetHeight(_imageView.frame);
[self creatView:CGRectMake(0, 0, width, y)];
[self creatView:CGRectMake(0, y, x, h)];
[self creatView:CGRectMake(0, y + h, width, height - y - h)];
[self creatView:CGRectMake(x + w, y, width - x - w, h)];
}
- (void)creatView:(CGRect)rect {
CGFloat alpha = 0.5;
UIColor *backColor = [UIColor blackColor];
UIView *view = [[UIView alloc] initWithFrame:rect];
view.backgroundColor = backColor;
view.alpha = alpha;
[self.view addSubview:view];
}
#pragma mark ============添加扫描效果============
- (void)addTimer
{
_timer = [NSTimer scheduledTimerWithTimeInterval:0.008 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
}
//控制扫描线上下滚动
- (void)timerMethod
{
if (upOrDown == NO) {
num ++;
_line.frame = CGRectMake(CGRectGetMinX(_imageView.frame)+5, CGRectGetMinY(_imageView.frame)+5+num, CGRectGetWidth(_imageView.frame)-10, 3);
if (num == (int)(CGRectGetHeight(_imageView.frame)-10)) {
upOrDown = YES;
}
}
else
{
num --;
_line.frame = CGRectMake(CGRectGetMinX(_imageView.frame)+5, CGRectGetMinY(_imageView.frame)+5+num, CGRectGetWidth(_imageView.frame)-10, 3);
if (num == 0) {
upOrDown = NO;
}
}
}
//暂定扫描
- (void)stopScan
{
//弹出提示框后,关闭扫描
[self.session stopRunning];
//弹出alert,关闭定时器
[_timer setFireDate:[NSDate distantFuture]];
//隐藏扫描线
_line.hidden = YES;
}
- (void)starScan
{
//开始扫描
[self.session startRunning];
//打开定时器
[_timer setFireDate:[NSDate distantPast]];
//显示扫描线
_line.hidden = NO;
}
@end
二维码生成
#pragma mark - 生成二维码
/**
生产二维码
@param memberId 生成二维码要用的字符串
*/
- (void)setUpDisPlayUserQRCodeWithID:(NSString*)memberId{
NSData *stringData = [memberId dataUsingEncoding:NSUTF8StringEncoding];
// 创建filter
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// 设置内容和纠错级别
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
// CIImage
CIImage *qrCIImage = qrFilter.outputImage;
CGRect extent = CGRectIntegral(qrCIImage.extent);
CGFloat scale = 200;
// 创建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:qrCIImage fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
_QRCodeImageView.image = [UIImage imageWithCGImage:scaledImage];
}
以上便是二维码的扫描和生成