文章目录

  • 前言
  • 正文
    • 1.定位二维码的位置
    • 2.扫码、解析
  • 总结

前言

业务中一直有扫码的需求,这次说需要扫多个码(详细一点是一图多码),有点东西的。
第一点:怎么做:拿到手第一反应是有没有什么库可以直接调用的,不动脑星人检索了一下Zbar和ZXing,ZXing不适合iOS、Zbar也没有一图多码的方法可以直接用的,况且引入库增加代价也高,最后,原生撸吧。
第二点:做成什么样:app有什么对标的,打开支某宝、微某的二维码扫码看看,平时自己也遇见过吧。
扫码:

  • 识别到一个码,跳转对应链接
  • 识别到多个码,显示二维码定位位置,点击定位图跳转

    第三点:划重点,原生扫码的方式很简单,难点在于如何定位到二维码的位置,上菜。

正文

1.定位二维码的位置

思路参考:二维码扫码效果(多个二维码识别和点选)

首先二维码识别原理是三个角标定位的,然后再通过角标读取内部信息;假设扫码layer是整个view,再将识别到的坐标信息转换到view上的坐标,这样就可以得到定位的信息进行绘图标注。

2.扫码、解析

扫码需要放到队列去,否则容易主线程阻塞。相机权限得先请求开启一波。

#import <AVFoundation/AVFoundation.h>
// AVCaptureMetadataOutputObjectsDelegate///主队列
#define GCD_main_queue dispatch_get_main_queue()
#define WeakSelf typeof(self) __weak weakSelf = self;// 输入输出中间桥梁(会话)
@property (strong, nonatomic) AVCaptureSession *session;
// 多个二维码 定位的数组
@property (strong, nonatomic) NSMutableArray *qrCodesArray;
//扫码点选回调
@property (nonatomic, copy) void(^clickQrCodeBlock)(NSString *qrStr);
//多个二维码位置点击按钮数组
@property (strong, nonatomic) NSMutableArray *qrCodesButtonArray;
// 重新扫码
@property (strong, nonatomic) UIButton *reScanButton;- (void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {if (granted) {dispatch_async(GCD_main_queue, ^{[self startRunning];// 扫码});}else{dispatch_async(GCD_main_queue, ^{//@"无法访问照相机,请在设置中打开相机权限"});}}];
}

重启扫码的btn,这里可以自由发挥

   self.reScanButton = [UIButton buttonWithType:UIButtonTypeCustom];//重新扫码self.reScanButton.backgroundColor = [UIColor blueColor];self.reScanButton.hidden = YES;[self.reScanButton setTitle:@"重新扫码" forState:UIControlStateNormal];self.reScanButton.backgroundColor = [UIColor blueColor];[self.cameraView addSubview:self.reScanButton];[self.reScanButton addTarget:self action:@selector(reScanBtnAction:) forControlEvents:UIControlEventTouchUpInside];[self.reScanButton mas_makeConstraints:^(MASConstraintMaker *make) {make.centerX.equalTo(self.cameraView);make.bottom.equalTo(self.cameraView.mas_bottom).offset(-60 * FTGetScreenScale());make.size.mas_equalTo(CGSizeMake(200 * FTGetScreenScale(), 60 * FTGetScreenScale()));}];self.qrCodesButtonArray = [NSMutableArray new];

干货:扫码开启和暂停

/**start running capture*/
- (void)startRunning {if(![self.session isRunning]){[self.session startRunning];}if(self.qrCodesButtonArray.count){//移除上一次的标记[self.qrCodesButtonArray enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) {[button removeFromSuperview];}];self.qrCodesButtonArray = [NSMutableArray new];self.reScanButton.hidden = YES;}}
/**stop running capture*/
- (void)stopRunning {// reload animation on mainThreadWeakSelfdispatch_async(dispatch_get_main_queue(), ^{[weakSelf.session stopRunning];});
}- (AVCaptureSession *)session {if (!_session) {//1.获取输入设备(摄像头)AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];//2.根据输入设备创建输入对象AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL];if (input == nil) {return nil;}//3.创建元数据的输出对象AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];//4.设置代理监听输出对象输出的数据,在主线程中刷新[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];// 5.创建会话(桥梁)AVCaptureSession *session = [[AVCaptureSession alloc]init];//实现高质量的输出和摄像,默认值为AVCaptureSessionPresetHigh,可以不写[session setSessionPreset:AVCaptureSessionPresetHigh];// 6.添加输入和输出到会话中(判断session是否已满)if ([session canAddInput:input]) {[session addInput:input];}if ([session canAddOutput:output]) {[session addOutput:output];}// 7.告诉输出对象, 需要输出什么样的数据 (二维码还是条形码等) 要先创建会话才能设置output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeCode93Code,AVMetadataObjectTypeCode39Code,AVMetadataObjectTypeCode39Mod43Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeUPCECode,AVMetadataObjectTypePDF417Code,AVMetadataObjectTypeAztecCode];// 8.创建预览图层AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];[previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];previewLayer.frame = self.cameraView.bounds;[self.cameraView.layer insertSublayer:previewLayer atIndex:0];//9.设置有效扫描区域,默认整个图层(很特别,1、要除以屏幕宽高比例,2、其中x和y、width和height分别互换位置)
//        CGRect rect = CGRectMake(kBgImgY/ScreenHeight, kBgImgX/ScreenWidth, kBgImgWidth/ScreenHeight, kBgImgWidth/ScreenWidth);_session = session;}return _session;
}#pragma mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{if ([metadataObjects count] >0){NSLog(@"========扫描后的url是:==============");NSMutableArray *muchArray = [NSMutableArray new];[metadataObjects enumerateObjectsUsingBlock:^(AVMetadataMachineReadableCodeObject *obj, NSUInteger idx, BOOL *stop) {if ([obj.type isEqualToString:AVMetadataObjectTypeQRCode]) {   //判断是否有数据,是否是二维码数据[muchArray addObject:obj];}}];[self stopRunning];// 我这里是扫码到结果就暂停扫码、加个重新扫描btn会用户友好一点,后面是处理扫码结果if([muchArray count] == 1){//扫描到一个二维码信息AVMetadataMachineReadableCodeObject * metadataObject = [muchArray objectAtIndex:0];NSString *stringValue = metadataObject.stringValue;self.urlString = stringValue.length?stringValue:@"";NSLog(@" 1111扫描后的url是:%@",self.urlString);if(self.urlString.length){dispatch_async(dispatch_get_main_queue(), ^{[self analyseResultAry:self.urlString];self.reScanButton.hidden = NO;});}}else if([muchArray count] >1){// 多个二维码信息 显示二维码定位页面  选择跳转self.qrCodesArray = [NSMutableArray new];[muchArray enumerateObjectsUsingBlock:^(AVMetadataMachineReadableCodeObject *result, NSUInteger idx, BOOL *stop) {NSMutableDictionary *dic = [NSMutableDictionary new];NSString *code = result.stringValue;[dic setObject:code forKey:@"code"];NSLog(@"2222 扫描后的url是:%@",code);// 标注多个二维码CGRect frame = [self makeFrameWithCodeObject:result Index:self.qrCodesArray.count];NSString *frameStr = NSStringFromCGRect(frame);[dic setObject:frameStr forKey:@"frame"];[self.qrCodesArray addObject:dic];//记录下标注的数组,下次扫码移除前面的标注}];dispatch_async(dispatch_get_main_queue(), ^{self.reScanButton.hidden = NO;});}}
}
//选择多个二维码中的一个
- (void)handleBtnAction:(UIButton *)sender {NSInteger index = sender.tag - 1000;if (index < self.qrCodesArray.count) {NSDictionary *dic = self.qrCodesArray[index];if([dic.allKeys containsObject:@"code"]){self.urlString = [dic objectForKey:@"code"]?[dic objectForKey:@"code"]:@"";NSLog(@"2222 扫描后的url是: 选中 %@",self.urlString);if(self.urlString.length){[self analyseResultAry:self.urlString];}}}
}//重新扫码
-(void)reScanBtnAction:(UIButton *)sender {[self startRunning];
}/*AVMetadataMachineReadableCodeObject,输出的点位坐标是其在原始数据流上的坐标,与屏幕视图坐标不一样,(坐标系,值都会有差别)将坐标值转为屏幕显示的图像视图(self.videoPreviewLayer)上的坐标值*/
-(CGRect)makeFrameWithCodeObject:(AVMetadataMachineReadableCodeObject *)objc Index:(NSInteger)index
{//将二维码坐标转化为扫码控件输出视图上的坐标//     CGSize isize = CGSizeMake(720.0, 1280.0); // 尺寸可以考虑不要写死,当前设置的是captureSession.sessionPreset = AVCaptureSessionPreset1280x720;CGSize isize = self.cameraView.frame.size; //扫码控件的输出尺寸,float Wout = 0.00;float Hout = 0.00;BOOL wMore = YES;/*取分辨率与输出的layer尺寸差,此处以AVLayerVideoGravityResizeAspectFill填充方式为例,判断扫描的范围更宽还是更长,并计算出超出部分的尺寸,后续计算减去这部分。如果是其它填充方式,计算方式不一样(比如AVLayerVideoGravityResizeAspect,则计算计算留白的尺寸,并后续补足这部分)*/if (isize.width/isize.height > self.cameraView.bounds.size.width/self.cameraView.bounds.size.height) {//当更宽时,计算扫描的坐标x为0 的点比输出视图的0点差多少(输出视图为全屏时,即屏幕外有多少)wMore = YES;Wout = (isize.width/isize.height)* self.cameraView.bounds.size.height;Wout = Wout - self.cameraView.bounds.size.width;Wout = Wout/2;}else{// 当更长时,计算y轴超出多少。wMore = NO;Hout = (isize.height/isize.width)* self.cameraView.bounds.size.width;Hout = Hout  - self.cameraView.bounds.size.height;Hout = Hout/2;}CGPoint point1 = CGPointZero;CGPoint point2 = CGPointZero;CGPoint point3 = CGPointZero;CGPoint point4 = CGPointZero;/*源坐标系下frame和角点,都是比例值,即源视频流尺寸下的百分比值。例子:frame :(x = 0.26720550656318665, y = 0.0014114481164142489), size = (width = 0.16406852006912231, height = 0.29584407806396484))objc.corners:{0.26823519751360592, 0.29203594744002659}{0.4312740177700658, 0.29725551905635411}{0.4294213439632073, 0.012761536345436197}{0.26720551457151021, 0.0014114481640513654}*/CGRect frame = objc.bounds;//在源坐标系的frame,NSArray *array = objc.corners;//源坐标系下二维码的角点CGPoint P = frame.origin;CGSize S = frame.size;//获取点for (int n = 0; n< array.count; n++) {CGPoint point = CGPointZero;CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[n]);CGPointMakeWithDictionaryRepresentation(dict, &point);
//        NSLog(@"二维码角点%@",NSStringFromCGPoint(point));//交换xy轴point.x = point.y +  point.x;point.y = point.x - point.y;point.x = point.x - point.y;//x轴反转point.x = (1-point.x);//point乘以比列。减去尺寸差,if (wMore) {point.x = (point.x * (isize.width/isize.height)* self.cameraView.bounds.size.height) - Wout;point.y = self.cameraView.bounds.size.height *(point.y);}else{point.x = self.cameraView.bounds.size.width *(point.x);point.y = (point.y) * (isize.height/isize.width)* self.cameraView.bounds.size.width - Hout;}if (n == 0) {point1 = point;}if (n == 1) {point2 = point;}if (n == 2) {point3 = point;}if (n == 3) {point4 = point;}}//通过获取最小和最大的X,Y值,二维码在视图上的frame(前面得到的点不一定是正方形的二维码,也可能是菱形的或者有一定旋转角度的)float minX = point1.x;minX = minX>point2.x?point2.x:minX;minX = minX>point3.x?point3.x:minX;minX = minX>point4.x?point4.x:minX;float minY = point1.y;minY = minY>point2.y?point2.y:minY;minY = minY>point3.y?point3.y:minY;minY = minY>point4.y?point4.y:minY;P.x = minX;P.y = minY;float maxX = point1.x;maxX = maxX<point2.x?point2.x:maxX;maxX = maxX<point3.x?point3.x:maxX;maxX = maxX<point4.x?point4.x:maxX;float maxY = point1.y;maxY = maxY<point2.y?point2.y:maxY;maxY = maxY<point3.y?point3.y:maxY;maxY = maxY<point4.y?point4.y:maxY;S.width = maxX - minX;S.height = maxY - minY;//y轴坐标方向调整CGRect QRFrame = CGRectMake(P.x , P.y  , S.width, S.height);UIButton *tempButton = [UIButton buttonWithType:UIButtonTypeCustom];//多个二维码添加选择btntempButton.backgroundColor = [UIColor blueColor];tempButton.frame = QRFrame;[self.cameraView addSubview:tempButton];tempButton.tag = 1000 + index;[tempButton addTarget:self action:@selector(handleBtnAction:) forControlEvents:UIControlEventTouchUpInside];[self.qrCodesButtonArray addObject:tempButton];return QRFrame;
}-(void)analyseResultAry:(NSString *)resultAsString{WeakSelf[[AFNetworkReachabilityManager sharedManager] startMonitoring];[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {[[AFNetworkReachabilityManager sharedManager] stopMonitoring];if (status >= 1) {// 有网络if (resultAsString.length) {//  结果干点啥~~~~												

iOS扫码一图多码原生处理AVCaptureSession相关推荐

  1. 微信扫码充值 php,PHP原生微信扫码支付

    素材火分享了多个微信支付源码,有用户需要一款PHP原生代码写的微信扫码支付,不基于任何框架,完全手写.需要其他支付源码的可找素材火管理员定制开发. 下载资源 下载积分: 800 积分 扫码支付只要授权 ...

  2. iOS扫码识别【自动拉近放大】(扫描较小二维码地过程中拉近放大图片)【光线暗的时候,能够自动打开闪光灯】

    文章目录 前言 I.搭建一个统一识别二维码的控制器 II.工具方法 QRCodeUtil see also AVMetadataObjectType 前言 先回顾下AVFoundation的扫码原理图 ...

  3. 一人之下ios扫码_一人之下ios版

    一人之下ios版是一款同名动漫改编格斗游戏,横版格斗行云流水的连击,解开未知的支线番外,感兴趣的玩家快来下载体验吧! 一人之下ios版游戏简介: <一人之下>同名动漫改编.异能格斗国潮手游 ...

  4. 一人之下ios扫码_一人之下手游ios版下载

    一人之下IOS版是一款3D动作RPG,继承了原作的画风,并穿插入动漫剧情,更是有着全程的中文配音,而在战斗系统上,游戏除了普通的格斗外,还有着丰富的职业和连招技能,更有着非常考验玩家反应能力的QTE系 ...

  5. 一人之下ios扫码_一人之下ios

    一人之下ios是一款由漫画改编而成的格斗类游戏.在游戏中,真实还原了剧情,以及人物特点,玩家自由选择角色人物,开启一段冒险之旅了,去挑战游戏中的关卡了.加上优美的音乐,会陶醉于游戏中,快来享受吧. 一 ...

  6. Flutter 最佳扫码插件

    插件已更新,支持自定义视图,具体请查看<Flutter 最佳扫码插件--自定义视图> 文章目录 扫码 用法 配置权限 iOS 权限请求 调用API 例子 TODO 插件开发 欢迎关注公众号 ...

  7. java实现简单扫码登录功能(模仿微信网页版扫码)

    java实现简单扫码登录功能 模仿微信pc网页版扫码登录 使用js代码生成qrcode二维码减轻服务器压力 js循环请求服务端,判断是否qrcode被扫 二维码超时失效功能 二维码被扫成功登录,服务端 ...

  8. iOS原生二维码扫码实现(含蒙版和扫码动画)

    #一.iOS实现原生扫码的意义 二维码扫码功能对于现在的iOS App开发来说是非常重要的. 通常为了节省开发时间,很多开发者会采用ZXing和ZBar等第三方SDK进行开发. 这样的好处是快速便捷, ...

  9. 用ios企业证书发布ipa到服务器上扫码下载

    这段时间公司需要做一个ios的app,用的是mui框架,在HBuilder中发行为原生的安装包,用的ios企业证书.我从ipa包生成之后说起. ipa包生成之后,就到了下载这一步了,因为是企业证书,上 ...

最新文章

  1. OpenCV实现遍历文件夹下所有文件
  2. ViewVC 1.1.16 发布,CVS/SVN的Web接口
  3. Redis进阶-Redis集群 【高可用切换】【cluster-require-full-coverage】集群是否完整才能对外提供服务
  4. 使用typedef声明新类型及函数指针
  5. 数据结构+算法——错题总结
  6. 使用git提交项目到码云
  7. Openfire3.9.3源代码导入eclipse中开发配置指南(转载)
  8. HTML 5 aside 标签
  9. Android 基本控件使用
  10. Java实现带发音的简易电子词典
  11. redmibook pro 14 arch linux alsamixer 检测不到声卡
  12. (Matlab实现)CNN卷积神经网络图片分类
  13. 初中高中睡前必看古诗名句
  14. 方差公式初三_九年级同步数学公式:方差公式(1)
  15. CMD命令查看WiFi密码
  16. 数字系统设计, 8个经典计数器电路方案合辑
  17. Windows和Linux入侵痕迹清理
  18. 域名CNAME记录不能同时适配根域名和www的解决方法
  19. 外卖订单量预测异常报警模型实践
  20. 【软考】【知识产权与法律法规】

热门文章

  1. Windows强行重置密码的影响
  2. Adaptec by PMC 8系列产品在Windows环境中的性能表现(二)
  3. 修改pptp默认端口号1723
  4. ABBYY Mac中如何实现PDF到Excel的快速转换
  5. js前端身份证完美验证
  6. spark提交命令中的jars设置方式
  7. DTL with变量
  8. 【Flutter从入门到入坑】Flutter 知识体系
  9. cv2.error: OpenCV(4.6.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window.cpp:967:
  10. 1052. Linked List Sorting (25)