QRCodeScanViewController.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. //
  2. // QRCodeScanViewController.m
  3. //
  4. //
  5. // Created by APPLE on 2023/9/19.
  6. //
  7. #import "QRCodeScanViewController.h"
  8. #import <AVFoundation/AVFoundation.h>
  9. #import <ImageIO/ImageIO.h>
  10. #import "GuideViewController.h"
  11. #import "RSATool.h"
  12. #import <TZImageManager.h>
  13. #import <TZImagePickerController.h>
  14. @interface QRCodeScanViewController ()<AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate,TZImagePickerControllerDelegate>
  15. @property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer;
  16. //捕捉会话
  17. @property(nonatomic,strong)AVCaptureSession *session;
  18. //辅助区域框
  19. @property(nonatomic,strong)UIImageView * cyanEdgeImageView;
  20. @property(nonatomic,strong)UIImageView * qrCodeScanLine;
  21. @property(nonatomic,strong)NSTimer * scanLineTimer;
  22. ////打开灯光btn,默认为hidden
  23. //
  24. //@property(nonatomic,strong)UIButton * lightBtn;
  25. //
  26. ////获取相册图片进行扫描
  27. //
  28. //@property(nonatomic,strong)UIButton * photoBtn;
  29. @end
  30. @implementation QRCodeScanViewController
  31. - (void)viewDidLoad {
  32. [super viewDidLoad];
  33. // Do any additional setup after loading the view.
  34. [self.navigationBar setHidden:YES];
  35. [self.toolBar setHidden:YES];
  36. AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  37. //判断摄像头状态是否可用
  38. if(authStatus==AVAuthorizationStatusAuthorized){
  39. mainBlock(^{
  40. [self startScan];
  41. });
  42. }else{
  43. NSLog(@"未开启相机权限,请前往设置中开启");
  44. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  45. if (granted){
  46. mainBlock(^{
  47. [self startScan];
  48. });
  49. }
  50. }];
  51. }
  52. }
  53. -(void)viewWillDisappear:(BOOL)animated
  54. {
  55. [super viewWillDisappear:animated];
  56. if(_scanLineTimer)[_scanLineTimer invalidate];
  57. }
  58. //开始扫描二维码
  59. -(void)startScan{
  60. //1.创建捕捉会话,AVCaptureSession是第一个要被创建的对象,所有的操作都要基于这一个session
  61. self.session = [[AVCaptureSession alloc]init];
  62. AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  63. AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
  64. [self.session addInput:input];
  65. //3.添加输出数据(示例对象-->类对象-->元类对象-->根元类对象)
  66. /*输入的类是AVCaptureInput,那么输出的类相应的就应该是AVCaptureOutput。
  67.  输出不需要和设备挂钩,因为一般情况下,我们的输出要么是音频或视频文件,要么是一些其他的数据,像二维码扫描一般是字符串类型。
  68.  所以创建AVCaptureOutput实例就不需要AVCaptureDevice对象。
  69.  AVCaptureOutput也同样是一个抽象类,同样要使用其子类,在这里我们扫描二维码,
  70.  使用的是AVCaptureMetadataOutput,设置代码如下所示*/
  71. AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
  72. [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  73. //设置能扫描的区域,这里注意,CGRectMake的x,y和width,height的值是互换位置
  74. output.rectOfInterest=CGRectMake(250/self.view.frame.size.height, 100/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width);
  75. [self.session addOutput:output];
  76. //设置输入元数据的类型(类型是二维码,条形码数据,注意,这个一定要写在添加到session后面,不然要崩溃,如果只需要扫描二维码只需要AVMetadataObjectTypeQRCode,如果还需要扫描条形码,那么全部添加上)
  77. [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,
  78. AVMetadataObjectTypeEAN8Code,
  79. AVMetadataObjectTypeEAN13Code,
  80. AVMetadataObjectTypeCode39Code,
  81. AVMetadataObjectTypeCode39Mod43Code,
  82. AVMetadataObjectTypeCode93Code,
  83. AVMetadataObjectTypeCode128Code,
  84. AVMetadataObjectTypePDF417Code,
  85. AVMetadataObjectTypeAztecCode,
  86. AVMetadataObjectTypeUPCECode,
  87. AVMetadataObjectTypeInterleaved2of5Code,
  88. AVMetadataObjectTypeITF14Code,
  89. AVMetadataObjectTypeDataMatrixCode,
  90. ]];
  91. //4.添加扫描图层
  92. self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
  93. self.layer.videoGravity=AVLayerVideoGravityResizeAspectFill;
  94. self.layer.frame = self.view.bounds;
  95. [self.view.layer addSublayer:self.layer];
  96. //5.创建view,通过layer层进行设置边框宽度和颜色,用来辅助展示扫描的区域
  97. _cyanEdgeImageView=[[UIImageView alloc] initWithFrame:CGRectMake(100, 250, self.view.frame.size.width-200, self.view.frame.size.width-200)];
  98. // _cyanEdgeImageView.layer.borderWidth=2;
  99. //
  100. // _cyanEdgeImageView.layer.borderColor =[UIColor cyanColor].CGColor;
  101. _cyanEdgeImageView.image = [UIImage imageNamed:@"qrCode_scan_bg"];
  102. [self.view addSubview:_cyanEdgeImageView];
  103. _qrCodeScanLine=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width-200, 1)];
  104. _qrCodeScanLine.image = [UIImage imageNamed:@"qrCode_scan_line"];
  105. [_cyanEdgeImageView addSubview:_qrCodeScanLine];
  106. _scanLineTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(scanLineDownAndUpFun) userInfo:nil repeats:YES];
  107. UILabel *tipLib = [[UILabel alloc] initWithFrame:CGRectMake(20, _cyanEdgeImageView.hw_max_y + 15, SCREEN_W - 40, 20)];
  108. tipLib.text = NSLocalizedString(@"guide_qrcoede_tips_please",nil);
  109. tipLib.font = [UIFont systemFontOfSize:14.0];
  110. tipLib.textAlignment = NSTextAlignmentCenter;
  111. tipLib.textColor = [UIColor whiteColor];
  112. [self.view addSubview:tipLib];
  113. //6.创建检测光感源
  114. AVCaptureVideoDataOutput *guangOutPut = [[AVCaptureVideoDataOutput alloc] init];
  115. [guangOutPut setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
  116. //设置为高质量采集率
  117. [self.session setSessionPreset:AVCaptureSessionPresetHigh];
  118. //把光感源添加到会话
  119. [self.session addOutput:guangOutPut];
  120. // self.lightBtn=[[UIButton alloc]initWithFrame:CGRectMake(150, cyanView.frame.origin.y+cyanView.frame.size.height+100, self.view.frame.size.width-300, self.view.frame.size.width-300)];
  121. //
  122. // self.lightBtn.backgroundColor=[UIColor grayColor];
  123. //
  124. // [self.lightBtn addTarget:self action:@selector(lightBtnClick:) forControlEvents:UIControlEventTouchUpInside];
  125. //
  126. //打开相册
  127. UIButton *openAlbunBut = [[UIButton alloc] init];
  128. [openAlbunBut setTitle:NSLocalizedString(@"guide_qrcoede_open_album",nil) forState:UIControlStateNormal];
  129. [openAlbunBut setTitleColor:[UIColor hwColor:@"#FFD315" alpha:1.0] forState:UIControlStateNormal];
  130. openAlbunBut.titleLabel.font = [UIFont systemFontOfSize:14.0];
  131. [openAlbunBut addTarget:self action:@selector(photoBtnClick:) forControlEvents:UIControlEventTouchUpInside];
  132. openAlbunBut.backgroundColor = [UIColor hwColor:@"#FFFFFF" alpha:0.2];
  133. [self.view addSubview:openAlbunBut];
  134. openAlbunBut.layer.cornerRadius = 24;
  135. openAlbunBut.layer.masksToBounds = YES;
  136. [openAlbunBut mas_makeConstraints:^(MASConstraintMaker *make) {
  137. make.width.mas_equalTo(140);
  138. make.height.mas_equalTo(48);
  139. make.centerX.mas_equalTo(0);
  140. make.bottom.mas_equalTo(-80);
  141. }];
  142. //9.开始扫描
  143. [self.session startRunning];
  144. //添加返回键
  145. CGFloat btn_w_h = 40;
  146. CGFloat btn_show = 28;
  147. UIButton *backBtn = [[UIButton alloc] init];
  148. [self.view addSubview:backBtn];
  149. [backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  150. make.top.mas_equalTo(H_STATE_BAR + (64.f - btn_w_h)/2.f);
  151. make.left.mas_equalTo(10);
  152. make.width.mas_equalTo(btn_w_h);
  153. make.height.mas_equalTo(btn_w_h);
  154. }];
  155. [backBtn setImage:[UIImage imageNamed:@"icon_white_back"] forState:(UIControlStateNormal)];
  156. [backBtn setImageEdgeInsets:(UIEdgeInsetsMake((btn_w_h - btn_show)/2.f, (btn_w_h - btn_show)/2.f, (btn_w_h - btn_show)/2.f, (btn_w_h - btn_show)/2.f))];
  157. [backBtn addTarget:self
  158. action:@selector(backBtnPressed)
  159. forControlEvents:(UIControlEventTouchUpInside)];
  160. }
  161. #pragma mark timer 处理线上下移动
  162. bool isDownType = YES;
  163. -(void)scanLineDownAndUpFun
  164. {
  165. [UIView animateWithDuration:0.01 animations:^{
  166. if(isDownType && self->_qrCodeScanLine.hw_y <= self->_cyanEdgeImageView.hw_h){
  167. self->_qrCodeScanLine.hw_y += 2;
  168. if(self->_cyanEdgeImageView.hw_h - self->_qrCodeScanLine.hw_y <= 5){
  169. isDownType = NO;
  170. }
  171. }
  172. else if(!isDownType && self->_qrCodeScanLine.hw_y >= 0)
  173. {
  174. self->_qrCodeScanLine.hw_y -= 2;
  175. if(self->_qrCodeScanLine.hw_y <= 5){
  176. isDownType = YES;
  177. }
  178. }
  179. }];
  180. }
  181. //实现扫描的回调代理方法
  182. - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{
  183. //如果数组metadataObjects中有数据,metadataObjects是个数组类型
  184. if(metadataObjects.count>0) {
  185. AVMetadataMachineReadableCodeObject*object = [metadataObjects lastObject];
  186. NSLog(@"%@",object.stringValue);
  187. /*扫描到有用信息时取消扫描*/
  188. NSString *resStr = object.stringValue;//RK3908P1V62112465
  189. [self handleScanCodeResultFun:resStr];
  190. }
  191. else{
  192. [[iToast makeText:NSLocalizedString(@"guide_qrcoede_tips_error",nil)] show];
  193. NSLog(@"没有扫描到数据");
  194. }
  195. }
  196. #pragma mark 处理扫码出来的数据
  197. - (void)handleScanCodeResultFun:(NSString*)resultStr
  198. {
  199. NSString * resStr = resultStr;
  200. if(resStr.length > 22)
  201. {
  202. NSString*desStr = [RSATool AES128Decrypt:resStr key:AESCODEKEEYY];
  203. if(desStr){//能解码
  204. resStr = desStr;
  205. }
  206. }
  207. //Ch0IKJCY9lOgokjfPBFE1cowdC1+tj7ywB2pZgSzjhc=
  208. //sgl5OzDoVjY5SmGuL50/EnQ4n6Kw+DzRiE1oUjq7yAM=
  209. //BT80240900050aac5bd5a23dd7cc
  210. // if (([resStr containsString:@"RK"] && (resStr.length == @"RK3908P1V62112465".length))
  211. // || resStr.length == @"0333933700223250017273".length
  212. // || resStr.length == @"BT80240900050aac5bd5a23dd7cc".length
  213. // )
  214. {
  215. [[iToast makeText:NSLocalizedString(@"guide_qrcoede_tips_ok",nil)] show];
  216. [_scanLineTimer invalidate];
  217. // NSDictionary *newDict = [[NSDictionary alloc] initWithObjectsAndKeys:resStr,Const_Have_Add_Device_SN, nil];
  218. // [HWDataManager setObjectWithKey:Const_Have_Add_Device value:newDict];
  219. [self nextStep:resStr];
  220. //停止扫描
  221. [self.session stopRunning];
  222. //移除扫描层layer
  223. [self.layer removeFromSuperlayer];
  224. }
  225. // else{
  226. // [[iToast makeText:NSLocalizedString(@"guide_qrcoede_tips_error",nil)] show];
  227. // }
  228. }
  229. - (void)nextStep:(NSString *)sn{
  230. KWeakSelf
  231. [[netWorkManager shareInstance] getThridMsgBySN:sn success:^(id _Nonnull responseObject) {
  232. DeviceThirdIdModel *model = responseObject;
  233. if([model isKindOfClass:[DeviceThirdIdModel class]]){
  234. if(model.status == 0 && model.data){
  235. [weakSelf gotoGuideViewFunBy:sn];
  236. }
  237. else{
  238. NSInteger state = 2;
  239. if (model.status == 201 || model.status == 202) {
  240. state = model.status;
  241. }
  242. if(self->_didScanErrorFun){
  243. self->_didScanErrorFun(state);
  244. }
  245. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  246. [self.navigationController popViewControllerAnimated:YES];
  247. });
  248. }
  249. }
  250. else{
  251. if(self->_didScanErrorFun){
  252. self->_didScanErrorFun(2);
  253. }
  254. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  255. [self.navigationController popViewControllerAnimated:YES];
  256. });
  257. }
  258. } failure:^(NSError * _Nonnull error) {
  259. if(self->_didScanErrorFun){
  260. self->_didScanErrorFun(0);
  261. }
  262. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  263. [self.navigationController popViewControllerAnimated:YES];
  264. });
  265. }];
  266. }
  267. #pragma mark GuideViewController
  268. - (void)gotoGuideViewFunBy:(NSString*)sn
  269. {
  270. //数据埋点
  271. [[netWorkManager shareInstance] DataEmbeddingPointBy:0 withEventValue:@"Scan_code"];
  272. NSDictionary *newDict = [[NSDictionary alloc] initWithObjectsAndKeys:sn,Const_Have_Add_Device_SN, nil];
  273. [HWDataManager setObjectWithKey:Const_Have_Add_Device value:newDict];
  274. [HWDataManager setBoolWithKey:stringKeyAddSn(Const_file_Transfe_working_background) value:YES];
  275. /*下一步*/
  276. GuideViewController *nextVC = [[GuideViewController alloc] init];
  277. nextVC.sn = sn;
  278. [self.navigationController pushViewController:nextVC animated:YES];
  279. }
  280. //光感传感器代理
  281. -(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{
  282. //获取光线的值
  283. CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
  284. NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
  285. CFRelease(metadataDict);
  286. NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
  287. float brightnessValue = [[exifMetadata objectForKey:(NSString*)kCGImagePropertyExifBrightnessValue]floatValue];
  288. NSLog(@"%f",brightnessValue);
  289. // 根据brightnessValue的值来打开和关闭闪光灯,一般值小于0就需要打开,大于0就关闭
  290. // if((brightnessValue <0)) {//显示闪光灯
  291. // self.lightBtn.hidden=NO;
  292. // }else if((brightnessValue >0)) {//隐藏闪光灯
  293. // self.lightBtn.hidden=YES;
  294. // }
  295. }
  296. ////闪光灯按钮点击方法
  297. //
  298. //-(void)lightBtnClick:(UIButton*)sender{
  299. // //判断当前设备是否有闪光灯
  300. //
  301. // AVCaptureDevice * device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  302. //
  303. // BOOL result=[device hasTorch];
  304. //
  305. // if(result==YES){
  306. // if(self.lightBtn.isSelected==NO){
  307. // self.lightBtn.selected=YES;
  308. // self.lightBtn.backgroundColor=[UIColor greenColor];
  309. // [device lockForConfiguration:nil];
  310. // [device setTorchMode: AVCaptureTorchModeOn];//开
  311. // [device unlockForConfiguration];
  312. // }else if(self.lightBtn.isSelected==YES){
  313. // self.lightBtn.selected=NO;
  314. // self.lightBtn.backgroundColor=[UIColor grayColor];
  315. // [device lockForConfiguration:nil];
  316. // [device setTorchMode: AVCaptureTorchModeOff];//关
  317. // [device unlockForConfiguration];
  318. // }
  319. // }else{
  320. // NSLog(@"当前设备闪光灯不可用");
  321. // }
  322. //}
  323. //
  324. ////获取相册图片btn点击方法
  325. -(void)photoBtnClick:(UIButton*)sender{
  326. // UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
  327. // imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  328. // // imagePicker.allowsEditing = YES;
  329. // imagePicker.delegate=self;
  330. // [self.navigationController presentViewController:imagePicker animated:YES completion:nil];
  331. [self photoClickFun];
  332. }
  333. - (void)photoClickFun
  334. {
  335. TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:self];
  336. imagePicker.allowPickingVideo=NO;
  337. //imagePicker.isStatusBarDefault=YES;
  338. imagePicker.allowTakePicture=NO;
  339. imagePicker.allowPreview = NO;
  340. // 4. 照片排列按修改时间升序
  341. imagePicker.sortAscendingByModificationDate = YES;
  342. [self presentViewController:imagePicker animated:YES completion:nil];
  343. }
  344. - (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto
  345. {
  346. if(photos && photos.count >= 1){
  347. [self detectImageFunBy:photos.firstObject];
  348. }
  349. else{
  350. [[iToast makeText:NSLocalizedString(@"guide_qrcoede_tips_error",nil)] show];
  351. }
  352. }
  353. //UIImagePickerControllerDelegate选择图片的回调
  354. -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
  355. //把UIimage类型转换成CIimage类型
  356. UIImage *pickedImage = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
  357. [picker dismissViewControllerAnimated:YES completion:^{
  358. }];
  359. [self detectImageFunBy:pickedImage];
  360. }
  361. #pragma mark 解析Uiimage
  362. - (void)detectImageFunBy:(UIImage*)pickedImage
  363. {
  364. CIImage*detectImage = [CIImage imageWithData:UIImagePNGRepresentation(pickedImage)];
  365. //解析扫描二维码结果字符串
  366. CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}];
  367. //CIQRCodeFeature*feature = (CIQRCodeFeature*)[detector featuresInImage:detectImage options:nil].firstObject;
  368. NSArray *messageStringArr = (CIQRCodeFeature*)[detector featuresInImage:detectImage options:nil];
  369. //排除掉 http的
  370. __block NSString *curQRCodeStr = nil;
  371. for(CIQRCodeFeature*feature in messageStringArr){
  372. if([feature.messageString rangeOfString:@"http"].location == NSNotFound){
  373. curQRCodeStr = feature.messageString;
  374. break;
  375. }
  376. }
  377. if(curQRCodeStr) {
  378. NSLog(@"=================二维码结果为%@",curQRCodeStr);
  379. [self handleScanCodeResultFun:curQRCodeStr];
  380. }else{
  381. [[iToast makeText:NSLocalizedString(@"guide_qrcoede_tips_error",nil)] show];
  382. NSLog(@"没有扫描到数据");
  383. }
  384. // //停止会话对象扫描
  385. // [self.session stopRunning];
  386. // //移除扫描层layer
  387. // [self.layer removeFromSuperlayer];
  388. }
  389. @end