scanToPCLoginViewController.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. //
  2. // scanToPCLoginViewController.m
  3. // Private-X
  4. //
  5. // Created by xd h on 2024/8/27.
  6. //
  7. #import "scanToPCLoginViewController.h"
  8. #import <AVFoundation/AVFoundation.h>
  9. #import <ImageIO/ImageIO.h>
  10. #import "PCLoginViewController.h"
  11. @interface scanToPCLoginViewController ()<AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,UINavigationControllerDelegate>
  12. @property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer;
  13. //捕捉会话
  14. @property(nonatomic,strong)AVCaptureSession *session;
  15. //辅助区域框
  16. @property(nonatomic,strong)UIImageView * cyanEdgeImageView;
  17. @property(nonatomic,strong)UIImageView * qrCodeScanLine;
  18. @property(nonatomic,strong)NSTimer * scanLineTimer;
  19. @end
  20. @implementation scanToPCLoginViewController
  21. - (void)viewDidLoad {
  22. [super viewDidLoad];
  23. // Do any additional setup after loading the view.
  24. [self.navigationBar setHidden:YES];
  25. [self.toolBar setHidden:YES];
  26. AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  27. //判断摄像头状态是否可用
  28. if(authStatus==AVAuthorizationStatusAuthorized){
  29. [self startScan];
  30. }else{
  31. NSLog(@"未开启相机权限,请前往设置中开启");
  32. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  33. dispatch_async(dispatch_get_main_queue(), ^{
  34. if (granted) {
  35. // 用户同意授权
  36. [self startScan];;
  37. } else {
  38. // 用户拒绝授权
  39. [self showPermissionDeniedAlert];
  40. }
  41. });
  42. }];
  43. }
  44. }
  45. - (void)showPermissionDeniedAlert {
  46. NSString * titleStr = NSLocalizedString(@"Camera_permissions_disabled",nil);
  47. NSString * tipStr = NSLocalizedString(@"Camera_permissions_open_tip",nil);
  48. UIAlertController *alert = [UIAlertController alertControllerWithTitle:titleStr
  49. message:tipStr
  50. preferredStyle:UIAlertControllerStyleAlert];
  51. NSString* cancelStr = NSLocalizedString(@"other_cancel",nil);
  52. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelStr style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  53. [self backBtnPressed];
  54. }];
  55. NSString*settingStr = NSLocalizedString(@"set_Privacy_button_title",nil);
  56. UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:settingStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  57. [self backBtnPressed];
  58. // 跳转到应用设置页面
  59. NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
  60. if ([[UIApplication sharedApplication] canOpenURL:settingsURL]) {
  61. [[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];
  62. }
  63. }];
  64. [alert addAction:cancelAction];
  65. [alert addAction:settingsAction];
  66. [self presentViewController:alert animated:YES completion:nil];
  67. }
  68. -(void)viewWillDisappear:(BOOL)animated
  69. {
  70. [super viewWillDisappear:animated];
  71. if(_scanLineTimer)[_scanLineTimer invalidate];
  72. }
  73. //开始扫描二维码
  74. -(void)startScan{
  75. //1.创建捕捉会话,AVCaptureSession是第一个要被创建的对象,所有的操作都要基于这一个session
  76. self.session = [[AVCaptureSession alloc]init];
  77. AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  78. AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
  79. [self.session addInput:input];
  80. //3.添加输出数据(示例对象-->类对象-->元类对象-->根元类对象)
  81. /*输入的类是AVCaptureInput,那么输出的类相应的就应该是AVCaptureOutput。
  82.  输出不需要和设备挂钩,因为一般情况下,我们的输出要么是音频或视频文件,要么是一些其他的数据,像二维码扫描一般是字符串类型。
  83.  所以创建AVCaptureOutput实例就不需要AVCaptureDevice对象。
  84.  AVCaptureOutput也同样是一个抽象类,同样要使用其子类,在这里我们扫描二维码,
  85.  使用的是AVCaptureMetadataOutput,设置代码如下所示*/
  86. AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
  87. [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  88. //设置能扫描的区域,这里注意,CGRectMake的x,y和width,height的值是互换位置
  89. 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);
  90. [self.session addOutput:output];
  91. //设置输入元数据的类型(类型是二维码,条形码数据,注意,这个一定要写在添加到session后面,不然要崩溃,如果只需要扫描二维码只需要AVMetadataObjectTypeQRCode,如果还需要扫描条形码,那么全部添加上)
  92. [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,
  93. AVMetadataObjectTypeEAN8Code,
  94. AVMetadataObjectTypeEAN13Code,
  95. AVMetadataObjectTypeCode39Code,
  96. AVMetadataObjectTypeCode39Mod43Code,
  97. AVMetadataObjectTypeCode93Code,
  98. AVMetadataObjectTypeCode128Code,
  99. AVMetadataObjectTypePDF417Code,
  100. AVMetadataObjectTypeAztecCode,
  101. AVMetadataObjectTypeUPCECode,
  102. AVMetadataObjectTypeInterleaved2of5Code,
  103. AVMetadataObjectTypeITF14Code,
  104. AVMetadataObjectTypeDataMatrixCode,
  105. ]];
  106. //4.添加扫描图层
  107. self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
  108. self.layer.videoGravity=AVLayerVideoGravityResizeAspectFill;
  109. self.layer.frame = self.view.bounds;
  110. [self.view.layer addSublayer:self.layer];
  111. //5.创建view,通过layer层进行设置边框宽度和颜色,用来辅助展示扫描的区域
  112. _cyanEdgeImageView=[[UIImageView alloc] initWithFrame:CGRectMake(100, 250, self.view.frame.size.width-200, self.view.frame.size.width-200)];
  113. // _cyanEdgeImageView.layer.borderWidth=2;
  114. //
  115. // _cyanEdgeImageView.layer.borderColor =[UIColor cyanColor].CGColor;
  116. _cyanEdgeImageView.image = [UIImage imageNamed:@"qrCode_scan_bg"];
  117. [self.view addSubview:_cyanEdgeImageView];
  118. _qrCodeScanLine=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width-200, 1)];
  119. _qrCodeScanLine.image = [UIImage imageNamed:@"qrCode_scan_line"];
  120. [_cyanEdgeImageView addSubview:_qrCodeScanLine];
  121. _scanLineTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(scanLineDownAndUpFun) userInfo:nil repeats:YES];
  122. UILabel *tipLib = [[UILabel alloc] initWithFrame:CGRectMake(20, _cyanEdgeImageView.hw_max_y + 15, SCREEN_W - 40, 20)];
  123. tipLib.text = NSLocalizedString(@"pc_qrcoede_tips_please",nil);
  124. tipLib.font = [UIFont systemFontOfSize:14.0];
  125. tipLib.textAlignment = NSTextAlignmentCenter;
  126. tipLib.textColor = [UIColor whiteColor];
  127. [self.view addSubview:tipLib];
  128. //6.创建检测光感源
  129. AVCaptureVideoDataOutput *guangOutPut = [[AVCaptureVideoDataOutput alloc] init];
  130. [guangOutPut setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
  131. //设置为高质量采集率
  132. [self.session setSessionPreset:AVCaptureSessionPresetHigh];
  133. //把光感源添加到会话
  134. [self.session addOutput:guangOutPut];
  135. //9.开始扫描
  136. [self.session startRunning];
  137. //添加返回键
  138. CGFloat btn_w_h = 40;
  139. CGFloat btn_show = 28;
  140. UIButton *backBtn = [[UIButton alloc] init];
  141. [self.view addSubview:backBtn];
  142. [backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  143. make.top.mas_equalTo(H_STATE_BAR + (64.f - btn_w_h)/2.f);
  144. make.left.mas_equalTo(10);
  145. make.width.mas_equalTo(btn_w_h);
  146. make.height.mas_equalTo(btn_w_h);
  147. }];
  148. [backBtn setImage:[UIImage imageNamed:@"icon_white_back"] forState:(UIControlStateNormal)];
  149. [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))];
  150. [backBtn addTarget:self
  151. action:@selector(backBtnPressed)
  152. forControlEvents:(UIControlEventTouchUpInside)];
  153. //添加标题
  154. CGFloat left_ritght = btn_w_h + 10 + 5;
  155. UILabel *titleLabel = [[UILabel alloc] init];
  156. titleLabel.text = NSLocalizedString(@"pc_scan_to_login",nil);
  157. titleLabel.textAlignment = NSTextAlignmentCenter;
  158. titleLabel.font = [UIFont systemFontOfSize:16.0];
  159. titleLabel.textColor = [UIColor whiteColor];
  160. [self.view addSubview:titleLabel];
  161. [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  162. make.centerY.mas_equalTo(backBtn.mas_centerY);
  163. make.left.mas_equalTo(left_ritght);
  164. make.right.mas_equalTo(-left_ritght);
  165. make.height.mas_equalTo(btn_w_h);
  166. }];
  167. }
  168. #pragma mark timer 处理线上下移动
  169. bool isPCScanDownType = YES;
  170. -(void)scanLineDownAndUpFun
  171. {
  172. [UIView animateWithDuration:0.01 animations:^{
  173. if(isPCScanDownType && self->_qrCodeScanLine.hw_y <= self->_cyanEdgeImageView.hw_h){
  174. self->_qrCodeScanLine.hw_y += 2;
  175. if(self->_cyanEdgeImageView.hw_h - self->_qrCodeScanLine.hw_y <= 5){
  176. isPCScanDownType = NO;
  177. }
  178. }
  179. else if(!isPCScanDownType && self->_qrCodeScanLine.hw_y >= 0)
  180. {
  181. self->_qrCodeScanLine.hw_y -= 2;
  182. if(self->_qrCodeScanLine.hw_y <= 5){
  183. isPCScanDownType = YES;
  184. }
  185. }
  186. }];
  187. }
  188. //实现扫描的回调代理方法
  189. - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{
  190. //如果数组metadataObjects中有数据,metadataObjects是个数组类型
  191. if(metadataObjects.count>0) {
  192. AVMetadataMachineReadableCodeObject*object = [metadataObjects lastObject];
  193. HLog(@"%@",object.stringValue);
  194. /*扫描到有用信息时取消扫描*/
  195. NSString *resStr = object.stringValue;//RK3908P1V62112465
  196. [self handleScanCodeResultFun:resStr];
  197. }
  198. else{
  199. [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show];
  200. HLog(@"没有扫描到数据");
  201. }
  202. }
  203. #pragma mark 处理扫码出来的数据
  204. - (void)handleScanCodeResultFun:(NSString*)resultStr
  205. {
  206. NSString * resStr = resultStr;
  207. if([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable)
  208. {
  209. [[iToast makeText:NSLocalizedString(@"phone_network_fail_Tips",nil)] show];
  210. return;
  211. }
  212. if ([resStr containsString:@"boxLoginId:"]){
  213. [_scanLineTimer invalidate];
  214. [self scanToPCloginFunWithID:resStr];
  215. //停止扫描
  216. [self.session stopRunning];
  217. //移除扫描层layer
  218. [self.layer removeFromSuperlayer];
  219. }
  220. else{
  221. [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show];
  222. }
  223. }
  224. //光感传感器代理
  225. -(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{
  226. //获取光线的值
  227. CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
  228. NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
  229. CFRelease(metadataDict);
  230. NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
  231. float brightnessValue = [[exifMetadata objectForKey:(NSString*)kCGImagePropertyExifBrightnessValue]floatValue];
  232. NSLog(@"%f",brightnessValue);
  233. // 根据brightnessValue的值来打开和关闭闪光灯,一般值小于0就需要打开,大于0就关闭
  234. // if((brightnessValue <0)) {//显示闪光灯
  235. // self.lightBtn.hidden=NO;
  236. // }else if((brightnessValue >0)) {//隐藏闪光灯
  237. // self.lightBtn.hidden=YES;
  238. // }
  239. }
  240. ////闪光灯按钮点击方法
  241. //
  242. //-(void)lightBtnClick:(UIButton*)sender{
  243. // //判断当前设备是否有闪光灯
  244. //
  245. // AVCaptureDevice * device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  246. //
  247. // BOOL result=[device hasTorch];
  248. //
  249. // if(result==YES){
  250. // if(self.lightBtn.isSelected==NO){
  251. // self.lightBtn.selected=YES;
  252. // self.lightBtn.backgroundColor=[UIColor greenColor];
  253. // [device lockForConfiguration:nil];
  254. // [device setTorchMode: AVCaptureTorchModeOn];//开
  255. // [device unlockForConfiguration];
  256. // }else if(self.lightBtn.isSelected==YES){
  257. // self.lightBtn.selected=NO;
  258. // self.lightBtn.backgroundColor=[UIColor grayColor];
  259. // [device lockForConfiguration:nil];
  260. // [device setTorchMode: AVCaptureTorchModeOff];//关
  261. // [device unlockForConfiguration];
  262. // }
  263. // }else{
  264. // NSLog(@"当前设备闪光灯不可用");
  265. // }
  266. //}
  267. #pragma mark PC 扫码登录
  268. -(void)scanToPCloginFunWithID:(NSString*)idStr
  269. {
  270. NSString *changSN = ksharedAppDelegate.DeviceThirdIdMod.data.changeSn;
  271. NSMutableDictionary *paraDict = [NSMutableDictionary new];
  272. [paraDict setValue:@1 forKey:@"status"];
  273. [paraDict setValue:idStr forKey:@"boxLoginId"];
  274. [paraDict setValue:changSN forKey:@"sn"];
  275. KWeakSelf
  276. [[netWorkManager shareInstance] CommonPostCallBackCode:updatePCLoginStateFun Parameters:paraDict success:^(id _Nonnull responseObject){
  277. SuperModel *curModel = [[SuperModel alloc] initWithDictionary:responseObject error:nil];
  278. if(curModel && curModel.status == 0){
  279. [weakSelf gotoNextVCFunWithID:idStr];
  280. }
  281. else
  282. {
  283. if(curModel.msg){
  284. [[iToast makeText:curModel.msg] show];
  285. }
  286. else{
  287. [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show];
  288. }
  289. [weakSelf.navigationController popViewControllerAnimated:YES];
  290. }
  291. } failure:^(NSError * _Nonnull error) {
  292. //[[iToast makeText:@"扫码信息错误"] show];
  293. [weakSelf.navigationController popViewControllerAnimated:YES];
  294. }];
  295. }
  296. #pragma mark 跳转下个界面
  297. - (void)gotoNextVCFunWithID:(NSString*)idStr
  298. {
  299. // NSArray *vcArr = self.navigationController.viewControllers;
  300. //
  301. // NSMutableArray * newArr = [NSMutableArray new];
  302. //
  303. // for (int i=0; i< vcArr.count-1; i++) {
  304. // UIViewController *vc = vcArr[i];
  305. // [newArr addObject:vc];
  306. // }
  307. //
  308. // self.navigationController.viewControllers = newArr;
  309. //
  310. // vcArr = self.navigationController.viewControllers;
  311. PCLoginViewController *nextVC = [PCLoginViewController new];
  312. nextVC.loginIdString = idStr;
  313. [self.navigationController pushViewController:nextVC animated:YES];
  314. }
  315. @end