今天看到一篇新的博客,介绍了系统自带的方法创建二维码扫描功能,所以立贴准备把自己二维码扫描相关的代码好步骤写出来 ! 待更新 !
一、原生二维码实现
**1)控制器代码相关**
1.导入 Framework : #import <AVFoundation/AVFoundation.h>
2.实现代理协议 :
AVCaptureMetadataOutputObjectsDelegate
UINavigationControllerDelegate
UIImagePickerControllerDelegate
3.属性相关
/// 二维码扫描相关
@property (strong,nonatomic)AVCaptureSession * session;
@property (strong,nonatomic)AVCaptureVideoPreviewLayer * previewLayer;
/*** 专门用于保存描边的图层 ***/
@property (nonatomic,strong) MSUScanView *scanView;
4.控制器里 : ViewDidLoad 里面实现相关方法
// 导航栏
[self createNavView];
// 中部视图
[self.view addSubview:self.scanningView];
[self setupMSUCodeScanning];
5.导航栏 (略)
6.二维码相关
- (void)setupMSUCodeScanning {
// (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.2, 0.2, 0.7, 0.6);
// 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.frame = CGRectMake(0, 64, WIDTH, HEIGHT-64);
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
//( 8)、将图层插入当前视图
[self.view.layer insertSublayer:_previewLayer atIndex:0];
// (9)、启动会话
[_session startRunning];
}
7.生命周期中相关操作及scanningView的初始化
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.scanView addTimer];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.scanView removeTimer];
}
- (void)dealloc {
NSLog(@"dealloc");
[self removeScanningView];
}
- (MSUScanView *)scanningView {
if (!_scanView) {
_scanView = [MSUScanView scanningViewWithFrame:CGRectMake(0, 64, WIDTH, HEIGHT-64) layer:self.view.layer];
}
return _scanView;
}
//移除扫描视图
- (void)removeScanningView {
[self.scanningView removeTimer];
[self.scanningView removeFromSuperview];
self.scanView = nil;
}
8.相册按钮相关 (MSUPermissionTool 为封装的权限工具类)
// 相册按钮点击
- (void)photoBtnClick:(UIButton *)sender{
// 1、 获取摄像设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (device) {
[MSUPermissionTool getPhotosPermission:^(NSInteger authStatus) {
if (authStatus == 1) {
dispatch_async(dispatch_get_main_queue(), ^{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //(选择类型)表示仅仅从相册中选取照片
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:^{
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
}];
});
}else{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"请去-> [设置 - 隐私 - 照片 - 秀贝] 打开访问开关" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//确定按钮点击事件处理
}]];
[self presentViewController:alert animated:YES completion:nil];
}
}];
}
}
9.代理相关
#pragma mark - 代理事件
#pragma mark -- 二维码代理(AVCaptureMetadataOutputObjectsDelegate)
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
// 扫描成功之后的提示音
[self MN_playSoundEffect:@"sound.caf"];
NSString *stringValue;
if ([metadataObjects count] >0){
//停止扫描
[_session stopRunning];
AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
stringValue = metadataObject.stringValue;
//代码封装抽离用 , 将扫描方法和扫描结果两块处理逻辑分开 此处为发送扫描结果
// [[NSNotificationCenter defaultCenter] postNotificationName:@"MSUCodeResultFromeScanning" object:metadataObject.stringValue];
if ([stringValue hasPrefix:@"http"]) {// 扫描结果为二维码
NSLog(@"二维码%@",stringValue);
} else { // 扫描结果为条形码
NSLog(@"条形码%@",stringValue);
}
}
}
/** 播放音效文件 */
- (void)MN_playSoundEffect:(NSString *)name {
// 获取音效
NSString *audioFile = [[NSBundle mainBundle] pathForResource:name ofType:nil];
NSURL *fileUrl = [NSURL fileURLWithPath:audioFile];
// 1、获得系统声音ID
SystemSoundID soundID = 0;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
// 2、播放音频
AudioServicesPlaySystemSound(soundID); // 播放音效
}
/** 播放完成回调函数 */
void soundCompleteCallback(SystemSoundID soundID, void *clientData){
//NSLog(@"播放完成...");
}
#pragma mark -- 相册代理(UIImagePickerControllerDelegate)
/** 相册选择 */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
[self.view addSubview:self.scanningView];
[self dismissViewControllerAnimated:YES completion:^{
[self scanMSUResultromPhotosInTheAlbum:[info objectForKey:@"UIImagePickerControllerOriginalImage"]];
}];
}
/** 相册取消 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
[self.view addSubview:self.scanningView];
[self dismissViewControllerAnimated:YES completion:nil];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}
/** 播放完成回调函数 */
- (void)scanMSUResultromPhotosInTheAlbum:(UIImage *)image{
// 对选取照片的处理,如果选取的图片尺寸过大,则压缩选取图片,否则不作处理
image = [UIImage imageSizeWithScreenImage:image];
// CIDetector(CIDetector可用于人脸识别)进行图片解析,从而使我们可以便捷的从相册中获取到二维码
// 声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
// 取得识别结果
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
for (int index = 0; index < [features count]; index ++) {
CIQRCodeFeature *feature = [features objectAtIndex:index];
NSString *stringValue = feature.messageString;
NSLog(@"scannedResult - - %@", stringValue);
//代码封装抽离用 , 将扫描方法和扫描结果两块处理逻辑分开 此处为发送扫描结果
// [[NSNotificationCenter defaultCenter] postNotificationName:@"MSUCodeResultFromeAibum" object:scannedResult];
// 处理相关逻辑地方
}
}
**2)View代码相关**
**.h文件相关diamante**
/**
* 对象方法创建SGQRCodeScanningView
*
* @param frame frame
* @param layer 父视图 layer
*/
- (instancetype)initWithFrame:(CGRect)frame layer:(CALayer *)layer;
/**
* 类方法创建SGQRCodeScanningView
*
* @param frame frame
* @param layer 父视图 layer
*/
+ (instancetype)scanningViewWithFrame:(CGRect )frame layer:(CALayer *)layer;
/** 添加定时器 */
- (void)addTimer;
/** 移除定时器(切记:一定要在Controller视图消失的时候,停止定时器) */
- (void)removeTimer;
**.m文件代码相关**
1.宏定义及属性相关
/** 扫描内容的Y值 */
#define scanContent_Y (self.frame.size.height) * 0.24
/** 扫描内容的X值 */
#define scanContent_X self.frame.size.width * 0.15
#define SGQRCodeScanningLineAnimation 0.05
#define NavHeight 64
@property (nonatomic, strong) AVCaptureDevice *device;
@property (nonatomic, strong) CALayer *tempLayer;
@property (nonatomic, strong) UIImageView *scanningline;
@property (nonatomic, strong) NSTimer *timer;
/** 扫描动画线(冲击波) 的高度 */
static CGFloat const scanninglineHeight = 12;
/** 扫描内容外部View的alpha值 */
static CGFloat const scanBorderOutsideViewAlpha = 0.4;
2.视图代码相关
- (CALayer *)tempLayer {
if (!_tempLayer) {
_tempLayer = [[CALayer alloc] init];
}
return _tempLayer;
}
- (instancetype)initWithFrame:(CGRect)frame layer:(CALayer *)layer {
if (self = [super initWithFrame:frame]) {
self.tempLayer = layer;
// 布局扫描界面
[self setupSubviews];
}
return self;
}
+ (instancetype)scanningViewWithFrame:(CGRect )frame layer:(CALayer *)layer {
return [[self alloc] initWithFrame:frame layer:layer];
}
- (void)setupSubviews {
// 扫描内容的创建
CALayer *scanContent_layer = [[CALayer alloc] init];
CGFloat scanContent_layerX = scanContent_X;
CGFloat scanContent_layerY = scanContent_Y;
CGFloat scanContent_layerW = self.frame.size.width - 2 * scanContent_X;
CGFloat scanContent_layerH = scanContent_layerW;
scanContent_layer.frame = CGRectMake(scanContent_layerX, scanContent_layerY, scanContent_layerW, scanContent_layerH);
scanContent_layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6].CGColor;
scanContent_layer.borderWidth = 0.7;
scanContent_layer.backgroundColor = [UIColor clearColor].CGColor;
[self.tempLayer addSublayer:scanContent_layer];
#pragma mark - - - 扫描外部View的创建
// 顶部layer的创建
CALayer *top_layer = [[CALayer alloc] init];
CGFloat top_layerX = 0;
CGFloat top_layerY = 0;
CGFloat top_layerW = self.frame.size.width;
CGFloat top_layerH = scanContent_layerY - NavHeight;
top_layer.frame = CGRectMake(top_layerX, top_layerY, top_layerW, top_layerH);
top_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
[self.layer addSublayer:top_layer];
// 左侧layer的创建
CALayer *left_layer = [[CALayer alloc] init];
CGFloat left_layerX = 0;
CGFloat left_layerY = scanContent_layerY - NavHeight;
CGFloat left_layerW = scanContent_X;
CGFloat left_layerH = scanContent_layerH;
left_layer.frame = CGRectMake(left_layerX, left_layerY, left_layerW, left_layerH);
left_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
[self.layer addSublayer:left_layer];
// 右侧layer的创建
CALayer *right_layer = [[CALayer alloc] init];
CGFloat right_layerX = CGRectGetMaxX(scanContent_layer.frame);
CGFloat right_layerY = scanContent_layerY - NavHeight;
CGFloat right_layerW = scanContent_X;
CGFloat right_layerH = scanContent_layerH;
right_layer.frame = CGRectMake(right_layerX, right_layerY, right_layerW, right_layerH);
right_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
[self.layer addSublayer:right_layer];
// 下面layer的创建
CALayer *bottom_layer = [[CALayer alloc] init];
CGFloat bottom_layerX = 0;
CGFloat bottom_layerY = CGRectGetMaxY(scanContent_layer.frame) - NavHeight;
CGFloat bottom_layerW = self.frame.size.width;
CGFloat bottom_layerH = self.frame.size.height - bottom_layerY;
bottom_layer.frame = CGRectMake(bottom_layerX, bottom_layerY, bottom_layerW, bottom_layerH);
bottom_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
[self.layer addSublayer:bottom_layer];
// 提示Label
UILabel *promptLabel = [[UILabel alloc] init];
promptLabel.backgroundColor = [UIColor clearColor];
CGFloat promptLabelX = 0;
CGFloat promptLabelY = CGRectGetMaxY(scanContent_layer.frame) + 30 - NavHeight;
CGFloat promptLabelW = self.frame.size.width;
CGFloat promptLabelH = 25;
promptLabel.frame = CGRectMake(promptLabelX, promptLabelY, promptLabelW, promptLabelH);
promptLabel.textAlignment = NSTextAlignmentCenter;
promptLabel.font = [UIFont boldSystemFontOfSize:13.0];
promptLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
promptLabel.text = @"将二维码/条码放入框内, 即可自动扫描";
[self addSubview:promptLabel];
// 添加闪光灯按钮
UIButton *light_button = [[UIButton alloc] init];
CGFloat light_buttonX = 0;
CGFloat light_buttonY = CGRectGetMaxY(promptLabel.frame) + scanContent_X * 0.5;
CGFloat light_buttonW = self.frame.size.width;
CGFloat light_buttonH = 25;
light_button.frame = CGRectMake(light_buttonX, light_buttonY, light_buttonW, light_buttonH);
[light_button setTitle:@"打开照明灯" forState:UIControlStateNormal];
[light_button setTitle:@"关闭照明灯" forState:UIControlStateSelected];
[light_button setTitleColor:promptLabel.textColor forState:(UIControlStateNormal)];
light_button.titleLabel.font = [UIFont systemFontOfSize:17];
[light_button addTarget:self action:@selector(light_buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:light_button];
#pragma mark - - - 扫描边角imageView的创建
// 左上侧的image
CGFloat margin = 7;
UIImage *left_image = [UIImage imageNamed:@"QRCodeLeftTop"];
UIImageView *left_imageView = [[UIImageView alloc] init];
CGFloat left_imageViewX = CGRectGetMinX(scanContent_layer.frame) - left_image.size.width * 0.5 + margin;
CGFloat left_imageViewY = CGRectGetMinY(scanContent_layer.frame) - left_image.size.width * 0.5 + margin;
CGFloat left_imageViewW = left_image.size.width;
CGFloat left_imageViewH = left_image.size.height;
left_imageView.frame = CGRectMake(left_imageViewX, left_imageViewY, left_imageViewW, left_imageViewH);
left_imageView.image = left_image;
[self.tempLayer addSublayer:left_imageView.layer];
// 右上侧的image
UIImage *right_image = [UIImage imageNamed:@"QRCodeRightTop"];
UIImageView *right_imageView = [[UIImageView alloc] init];
CGFloat right_imageViewX = CGRectGetMaxX(scanContent_layer.frame) - right_image.size.width * 0.5 - margin;
CGFloat right_imageViewY = left_imageView.frame.origin.y;
CGFloat right_imageViewW = left_image.size.width;
CGFloat right_imageViewH = left_image.size.height;
right_imageView.frame = CGRectMake(right_imageViewX, right_imageViewY, right_imageViewW, right_imageViewH);
right_imageView.image = right_image;
[self.tempLayer addSublayer:right_imageView.layer];
// 左下侧的image
UIImage *left_image_down = [UIImage imageNamed:@"QRCodeLeftBottom"];
UIImageView *left_imageView_down = [[UIImageView alloc] init];
CGFloat left_imageView_downX = left_imageView.frame.origin.x;
CGFloat left_imageView_downY = CGRectGetMaxY(scanContent_layer.frame) - left_image_down.size.width * 0.5 - margin;
CGFloat left_imageView_downW = left_image.size.width;
CGFloat left_imageView_downH = left_image.size.height;
left_imageView_down.frame = CGRectMake(left_imageView_downX, left_imageView_downY, left_imageView_downW, left_imageView_downH);
left_imageView_down.image = left_image_down;
[self.tempLayer addSublayer:left_imageView_down.layer];
// 右下侧的image
UIImage *right_image_down = [UIImage imageNamed:@"QRCodeRightBottom"];
UIImageView *right_imageView_down = [[UIImageView alloc] init];
CGFloat right_imageView_downX = right_imageView.frame.origin.x;
CGFloat right_imageView_downY = left_imageView_down.frame.origin.y;
CGFloat right_imageView_downW = left_image.size.width;
CGFloat right_imageView_downH = left_image.size.height;
right_imageView_down.frame = CGRectMake(right_imageView_downX, right_imageView_downY, right_imageView_downW, right_imageView_downH);
right_imageView_down.image = right_image_down;
[self.tempLayer addSublayer:right_imageView_down.layer];
}
#pragma mark - - - 照明灯的点击事件
- (void)light_buttonAction:(UIButton *)button {
if (button.selected == NO) { // 点击打开照明灯
[self turnOnLight:YES];
button.selected = YES;
} else { // 点击关闭照明灯
[self turnOnLight:NO];
button.selected = NO;
}
}
- (void)turnOnLight:(BOOL)on {
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([_device hasTorch]) {
[_device lockForConfiguration:nil];
if (on) {
[_device setTorchMode:AVCaptureTorchModeOn];
} else {
[_device setTorchMode: AVCaptureTorchModeOff];
}
[_device unlockForConfiguration];
}
}
- (UIImageView *)scanningline {
if (!_scanningline) {
_scanningline = [[UIImageView alloc] init];
_scanningline.image = [UIImage imageNamed:@"QRCodeScanningLine"];
_scanningline.frame = CGRectMake(scanContent_X * 0.5, scanContent_Y, self.frame.size.width - scanContent_X , scanninglineHeight);
}
return _scanningline;
}
- (void)addScanningline {
// 扫描动画添加
[self.tempLayer addSublayer:self.scanningline.layer];
}
#pragma mark - - - 添加定时器
- (void)addTimer {
[self addScanningline];
self.timer = [NSTimer scheduledTimerWithTimeInterval:SGQRCodeScanningLineAnimation target:self selector:@selector(timeAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
#pragma mark - - - 移除定时器
- (void)removeTimer {
[self.timer invalidate];
self.timer = nil;
[self.scanningline removeFromSuperview];
self.scanningline = nil;
}
#pragma mark - - - 执行定时器方法
- (void)timeAction {
__block CGRect frame = _scanningline.frame;
static BOOL flag = YES;
if (flag) {
frame.origin.y = scanContent_Y;
flag = NO;
[UIView animateWithDuration:SGQRCodeScanningLineAnimation animations:^{
frame.origin.y += 5;
_scanningline.frame = frame;
} completion:nil];
} else {
if (_scanningline.frame.origin.y >= scanContent_Y) {
CGFloat scanContent_MaxY = scanContent_Y + self.frame.size.width - 2 * scanContent_X;
if (_scanningline.frame.origin.y >= scanContent_MaxY - 10) {
frame.origin.y = scanContent_Y;
_scanningline.frame = frame;
flag = YES;
} else {
[UIView animateWithDuration:SGQRCodeScanningLineAnimation animations:^{
frame.origin.y += 5;
_scanningline.frame = frame;
} completion:nil];
}
} else {
flag = !flag;
}
}
}