// // scanToPCLoginViewController.m // Private-X // // Created by xd h on 2024/8/27. // #import "scanToPCLoginViewController.h" #import #import #import "PCLoginViewController.h" @interface scanToPCLoginViewController () @property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer; //捕捉会话 @property(nonatomic,strong)AVCaptureSession *session; //辅助区域框 @property(nonatomic,strong)UIImageView * cyanEdgeImageView; @property(nonatomic,strong)UIImageView * qrCodeScanLine; @property(nonatomic,strong)NSTimer * scanLineTimer; @end @implementation scanToPCLoginViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.navigationBar setHidden:YES]; [self.toolBar setHidden:YES]; AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; //判断摄像头状态是否可用 if(authStatus==AVAuthorizationStatusAuthorized){ [self startScan]; }else{ NSLog(@"未开启相机权限,请前往设置中开启"); [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { // 用户同意授权 [self startScan];; } else { // 用户拒绝授权 [self showPermissionDeniedAlert]; } }); }]; } } - (void)showPermissionDeniedAlert { NSString * titleStr = NSLocalizedString(@"Camera_permissions_disabled",nil); NSString * tipStr = NSLocalizedString(@"Camera_permissions_open_tip",nil); UIAlertController *alert = [UIAlertController alertControllerWithTitle:titleStr message:tipStr preferredStyle:UIAlertControllerStyleAlert]; NSString* cancelStr = NSLocalizedString(@"other_cancel",nil); UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelStr style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [self backBtnPressed]; }]; NSString*settingStr = NSLocalizedString(@"set_Privacy_button_title",nil); UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:settingStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self backBtnPressed]; // 跳转到应用设置页面 NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:settingsURL]) { [[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil]; } }]; [alert addAction:cancelAction]; [alert addAction:settingsAction]; [self presentViewController:alert animated:YES completion:nil]; } -(void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if(_scanLineTimer)[_scanLineTimer invalidate]; } //开始扫描二维码 -(void)startScan{ //1.创建捕捉会话,AVCaptureSession是第一个要被创建的对象,所有的操作都要基于这一个session self.session = [[AVCaptureSession alloc]init]; AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; [self.session addInput:input]; //3.添加输出数据(示例对象-->类对象-->元类对象-->根元类对象) /*输入的类是AVCaptureInput,那么输出的类相应的就应该是AVCaptureOutput。  输出不需要和设备挂钩,因为一般情况下,我们的输出要么是音频或视频文件,要么是一些其他的数据,像二维码扫描一般是字符串类型。  所以创建AVCaptureOutput实例就不需要AVCaptureDevice对象。  AVCaptureOutput也同样是一个抽象类,同样要使用其子类,在这里我们扫描二维码,  使用的是AVCaptureMetadataOutput,设置代码如下所示*/ AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; //设置能扫描的区域,这里注意,CGRectMake的x,y和width,height的值是互换位置 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); [self.session addOutput:output]; //设置输入元数据的类型(类型是二维码,条形码数据,注意,这个一定要写在添加到session后面,不然要崩溃,如果只需要扫描二维码只需要AVMetadataObjectTypeQRCode,如果还需要扫描条形码,那么全部添加上) [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode39Mod43Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeAztecCode, AVMetadataObjectTypeUPCECode, AVMetadataObjectTypeInterleaved2of5Code, AVMetadataObjectTypeITF14Code, AVMetadataObjectTypeDataMatrixCode, ]]; //4.添加扫描图层 self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; self.layer.videoGravity=AVLayerVideoGravityResizeAspectFill; self.layer.frame = self.view.bounds; [self.view.layer addSublayer:self.layer]; //5.创建view,通过layer层进行设置边框宽度和颜色,用来辅助展示扫描的区域 _cyanEdgeImageView=[[UIImageView alloc] initWithFrame:CGRectMake(100, 250, self.view.frame.size.width-200, self.view.frame.size.width-200)]; // _cyanEdgeImageView.layer.borderWidth=2; // // _cyanEdgeImageView.layer.borderColor =[UIColor cyanColor].CGColor; _cyanEdgeImageView.image = [UIImage imageNamed:@"qrCode_scan_bg"]; [self.view addSubview:_cyanEdgeImageView]; _qrCodeScanLine=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width-200, 1)]; _qrCodeScanLine.image = [UIImage imageNamed:@"qrCode_scan_line"]; [_cyanEdgeImageView addSubview:_qrCodeScanLine]; _scanLineTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(scanLineDownAndUpFun) userInfo:nil repeats:YES]; UILabel *tipLib = [[UILabel alloc] initWithFrame:CGRectMake(20, _cyanEdgeImageView.hw_max_y + 15, SCREEN_W - 40, 20)]; tipLib.text = NSLocalizedString(@"pc_qrcoede_tips_please",nil); tipLib.font = [UIFont systemFontOfSize:14.0]; tipLib.textAlignment = NSTextAlignmentCenter; tipLib.textColor = [UIColor whiteColor]; [self.view addSubview:tipLib]; //6.创建检测光感源 AVCaptureVideoDataOutput *guangOutPut = [[AVCaptureVideoDataOutput alloc] init]; [guangOutPut setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; //设置为高质量采集率 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; //把光感源添加到会话 [self.session addOutput:guangOutPut]; //9.开始扫描 [self.session startRunning]; //添加返回键 CGFloat btn_w_h = 40; CGFloat btn_show = 28; UIButton *backBtn = [[UIButton alloc] init]; [self.view addSubview:backBtn]; [backBtn mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(H_STATE_BAR + (64.f - btn_w_h)/2.f); make.left.mas_equalTo(10); make.width.mas_equalTo(btn_w_h); make.height.mas_equalTo(btn_w_h); }]; [backBtn setImage:[UIImage imageNamed:@"icon_white_back"] forState:(UIControlStateNormal)]; [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))]; [backBtn addTarget:self action:@selector(backBtnPressed) forControlEvents:(UIControlEventTouchUpInside)]; //添加标题 CGFloat left_ritght = btn_w_h + 10 + 5; UILabel *titleLabel = [[UILabel alloc] init]; titleLabel.text = NSLocalizedString(@"pc_scan_to_login",nil); titleLabel.textAlignment = NSTextAlignmentCenter; titleLabel.font = [UIFont systemFontOfSize:16.0]; titleLabel.textColor = [UIColor whiteColor]; [self.view addSubview:titleLabel]; [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.mas_equalTo(backBtn.mas_centerY); make.left.mas_equalTo(left_ritght); make.right.mas_equalTo(-left_ritght); make.height.mas_equalTo(btn_w_h); }]; } #pragma mark timer 处理线上下移动 bool isPCScanDownType = YES; -(void)scanLineDownAndUpFun { [UIView animateWithDuration:0.01 animations:^{ if(isPCScanDownType && self->_qrCodeScanLine.hw_y <= self->_cyanEdgeImageView.hw_h){ self->_qrCodeScanLine.hw_y += 2; if(self->_cyanEdgeImageView.hw_h - self->_qrCodeScanLine.hw_y <= 5){ isPCScanDownType = NO; } } else if(!isPCScanDownType && self->_qrCodeScanLine.hw_y >= 0) { self->_qrCodeScanLine.hw_y -= 2; if(self->_qrCodeScanLine.hw_y <= 5){ isPCScanDownType = YES; } } }]; } //实现扫描的回调代理方法 - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{ //如果数组metadataObjects中有数据,metadataObjects是个数组类型 if(metadataObjects.count>0) { AVMetadataMachineReadableCodeObject*object = [metadataObjects lastObject]; HLog(@"%@",object.stringValue); /*扫描到有用信息时取消扫描*/ NSString *resStr = object.stringValue;//RK3908P1V62112465 [self handleScanCodeResultFun:resStr]; } else{ [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show]; HLog(@"没有扫描到数据"); } } #pragma mark 处理扫码出来的数据 - (void)handleScanCodeResultFun:(NSString*)resultStr { NSString * resStr = resultStr; if([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) { [[iToast makeText:NSLocalizedString(@"phone_network_fail_Tips",nil)] show]; return; } if ([resStr containsString:@"boxLoginId:"]){ [_scanLineTimer invalidate]; [self scanToPCloginFunWithID:resStr]; //停止扫描 [self.session stopRunning]; //移除扫描层layer [self.layer removeFromSuperlayer]; } else{ [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show]; } } //光感传感器代理 -(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{ //获取光线的值 CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate); NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict]; CFRelease(metadataDict); NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy]; float brightnessValue = [[exifMetadata objectForKey:(NSString*)kCGImagePropertyExifBrightnessValue]floatValue]; NSLog(@"%f",brightnessValue); // 根据brightnessValue的值来打开和关闭闪光灯,一般值小于0就需要打开,大于0就关闭 // if((brightnessValue <0)) {//显示闪光灯 // self.lightBtn.hidden=NO; // }else if((brightnessValue >0)) {//隐藏闪光灯 // self.lightBtn.hidden=YES; // } } ////闪光灯按钮点击方法 // //-(void)lightBtnClick:(UIButton*)sender{ // //判断当前设备是否有闪光灯 // // AVCaptureDevice * device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // // BOOL result=[device hasTorch]; // // if(result==YES){ // if(self.lightBtn.isSelected==NO){ // self.lightBtn.selected=YES; // self.lightBtn.backgroundColor=[UIColor greenColor]; // [device lockForConfiguration:nil]; // [device setTorchMode: AVCaptureTorchModeOn];//开 // [device unlockForConfiguration]; // }else if(self.lightBtn.isSelected==YES){ // self.lightBtn.selected=NO; // self.lightBtn.backgroundColor=[UIColor grayColor]; // [device lockForConfiguration:nil]; // [device setTorchMode: AVCaptureTorchModeOff];//关 // [device unlockForConfiguration]; // } // }else{ // NSLog(@"当前设备闪光灯不可用"); // } //} #pragma mark PC 扫码登录 -(void)scanToPCloginFunWithID:(NSString*)idStr { NSString *changSN = ksharedAppDelegate.DeviceThirdIdMod.data.changeSn; NSMutableDictionary *paraDict = [NSMutableDictionary new]; [paraDict setValue:@1 forKey:@"status"]; [paraDict setValue:idStr forKey:@"boxLoginId"]; [paraDict setValue:changSN forKey:@"sn"]; KWeakSelf [[netWorkManager shareInstance] CommonPostCallBackCode:updatePCLoginStateFun Parameters:paraDict success:^(id _Nonnull responseObject){ SuperModel *curModel = [[SuperModel alloc] initWithDictionary:responseObject error:nil]; if(curModel && curModel.status == 0){ [weakSelf gotoNextVCFunWithID:idStr]; } else { if(curModel.msg){ [[iToast makeText:curModel.msg] show]; } else{ [[iToast makeText:NSLocalizedString(@"pc_qrcoede_tips_error",nil)] show]; } [weakSelf.navigationController popViewControllerAnimated:YES]; } } failure:^(NSError * _Nonnull error) { //[[iToast makeText:@"扫码信息错误"] show]; [weakSelf.navigationController popViewControllerAnimated:YES]; }]; } #pragma mark 跳转下个界面 - (void)gotoNextVCFunWithID:(NSString*)idStr { // NSArray *vcArr = self.navigationController.viewControllers; // // NSMutableArray * newArr = [NSMutableArray new]; // // for (int i=0; i< vcArr.count-1; i++) { // UIViewController *vc = vcArr[i]; // [newArr addObject:vc]; // } // // self.navigationController.viewControllers = newArr; // // vcArr = self.navigationController.viewControllers; PCLoginViewController *nextVC = [PCLoginViewController new]; nextVC.loginIdString = idStr; [self.navigationController pushViewController:nextVC animated:YES]; } @end