// // SPSBFilmingIdentityCardViewController.m // 我的社保 // // Created by shanp on 2021/5/18. // #import "SPSBFilmingIdentityCardViewController.h" #import #import #import "SPSBUIGeneralHeader.h" #import #import "UIView+SPSBLoadingTipsView.h" #import #import "SPSBConfirmAlertViewController.h" #import "SPSBSelectSinglePhotoViewController.h" @interface SPSBFilmingIdentityCardViewController () { AVCaptureSession *_session; AVCaptureStillImageOutput *_dataOutput; AVCaptureDeviceInput * _dataInput; AVCaptureVideoPreviewLayer *_previewlayer; UIImageView *_imageView; CGFloat _topShadeViewHeight; CGFloat _leftShadViewWidth; CGFloat _bottomShadeViewHeight; bool _canOpenCamera; bool _alert; UIView *_tipsLabel; } @end @implementation SPSBFilmingIdentityCardViewController - (instancetype)init { self = [super init]; if (!self) return nil; self.modalPresentationStyle = UIModalPresentationFullScreen; return self; } - (void)viewDidLoad { [super viewDidLoad]; [self setUp]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (_tipsLabel) { [UIView animateWithDuration:0.3 delay:2 options:0 animations:^{ self->_tipsLabel.alpha = 0; } completion:^(BOOL finished) { [self->_tipsLabel removeFromSuperview]; self->_tipsLabel = nil; }]; } if (!_canOpenCamera) { if (_alert) { [self backAction]; return; } _alert = true; SPSBConfirmAlertViewController *vc = [SPSBConfirmAlertViewController alertWithTitle:@"提示" content:@"不能打开相机,是否直接进入相册"]; @weakify(self) [vc setCancelButtonTitle:@"暂时不进入" titleColor:nil action:^{ @strongify(self) [self backAction]; }]; [vc setConfirmButtonTitle:@"立即进入" titleColor:nil action:^{ @strongify(self) [self openPhotoAlbum]; }]; [self presentViewController:vc animated:false completion:nil]; } } #pragma mark - Action - (void)setUp { //-- Setup Capture Session. _session = [[AVCaptureSession alloc] init]; [_session beginConfiguration]; //-- Set preset session size. if ([_session canSetSessionPreset:AVCaptureSessionPresetHigh]) {//设置分辨率 _session.sessionPreset = AVCaptureSessionPresetHigh; } //-- Creata a video device and input from that Device. Add the input to the capture session. AVCaptureDevice * videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error; _dataInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error]; if (error) { _canOpenCamera = false; return; } _canOpenCamera = true; [_session addInput:_dataInput]; _previewlayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session]; _previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill; _previewlayer.frame = self.view.bounds; [self.view.layer insertSublayer:_previewlayer atIndex:0]; [self setupUI]; //-- Create the output for the capture session. _dataOutput = [[AVCaptureStillImageOutput alloc] init]; [_session addOutput:_dataOutput]; [_session commitConfiguration]; [_session startRunning]; } - (void)takePhotoAction { AVCaptureConnection *videoConnection = nil; for (AVCaptureConnection *connection in _dataOutput.connections) { for (AVCaptureInputPort *port in [connection inputPorts]) { if ([[port mediaType] isEqual:AVMediaTypeVideo] ) { videoConnection = connection; //先找出目标connections(见图) break; } } if (videoConnection) { break; } } //锁定connection后就可以获取静态图了。 @weakify(self) [_dataOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error) { @strongify(self) if (imageDataSampleBuffer) { NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *image = [[UIImage alloc] initWithData:imageData]; self->_imageView.image = image; [self handleImage:image]; [self->_session stopRunning]; } else { [self.view showToastWithTitle:@"获取图像失败请重试"]; } }]; } - (void)handleImage:(UIImage *)image { self.view.userInteractionEnabled = false; [self.view showLoadingToastWithTitle:@"正在处理" userClick:false]; @weakify(self) dispatch_async(dispatch_get_global_queue(0, 0), ^{ @strongify(self) debugLog(@"%lf-----%lf", image.size.width, image.size.height); debugLog(@"%lf-----%lf", jxh_screenWidth(), jxh_screenHeight()); UIImage *_image = [UIImage fixOrientation:image]; CGFloat scale = _image.size.height / jxh_screenHeight(); CGFloat x = (_image.size.width - _image.size.height * jxh_screenWidth() / jxh_screenHeight()) / 2 + self->_leftShadViewWidth * scale; CGFloat width = (jxh_screenWidth() - self->_leftShadViewWidth * 2) * scale; CGImageRef imageRef = CGImageCreateWithImageInRect(_image.CGImage, CGRectMake(x, self->_topShadeViewHeight * scale, width, width * SPSBIdentifierCardScale)); _image = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); _image = [self changeTheDirectionOfTheImage:_image]; _image = [self scaleImage:_image]; @weakify(self) dispatch_async(dispatch_get_main_queue(), ^{ [self.view dismissToast]; [self dismissViewControllerAnimated:true completion:^{ @strongify(self) if (self->_spsb_caughtImage) { self->_spsb_caughtImage(_image); } }]; }); }); } - (UIImage *)scaleImage:(UIImage *)image { UIImage *img = [UIImage imageWithImage:image scale:480]; debugLog(@"%lf-----%lf", img.size.width, img.size.height); return img; } - (UIImage *)changeTheDirectionOfTheImage:(UIImage *)image { CGFloat rotate = M_PI_2; CGRect rect = CGRectMake(0, 0, image.size.height, image.size.width); CGFloat translateX = 0; CGFloat translateY = -rect.size.width; CGFloat scaleY = rect.size.width/rect.size.height; CGFloat scaleX = rect.size.height/rect.size.width; UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); //做CTM变换 CGContextTranslateCTM(context, 0.0, rect.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextRotateCTM(context, rotate); CGContextTranslateCTM(context, translateX, translateY); CGContextScaleCTM(context, scaleX, scaleY); //绘制图片 CGContextDrawImage(context, CGRectMake(0, 0, rect.size.width, rect.size.height), image.CGImage); UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); return img; } //打开相册 - (void)openPhotoAlbum { // if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; imagePicker.modalPresentationStyle = UIModalPresentationFullScreen; imagePicker.delegate = self; imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [imagePicker setEditing:true animated:true]; //允许编辑 [self presentViewController:imagePicker animated:true completion:^{ }]; } else { [self.view showToastWithTitle:@"相册无法打开"]; [self dismissViewControllerAnimated:true completion:nil]; } } - (void)backAction { [self dismissViewControllerAnimated:true completion:nil]; } #pragma mark - UIImagePickerControllerDelegate //打开相册之后选择的方法 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { //获得媒体类型 NSString *mediaType = [info valueForKey:UIImagePickerControllerMediaType]; if ([mediaType hasSuffix:@"image"]) { UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage]; if (image) { [self pushSelectSinglePhotoViewControllerWithImage:image controller:picker]; return; } NSURL *url = [info valueForKey:UIImagePickerControllerReferenceURL]; NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString]; __block NSString *localIdentifiers; [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj.name isEqualToString:@"id"]) { localIdentifiers = obj.value; *stop = true; } }]; if (localIdentifiers) { PHFetchResult * re = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifiers] options:nil]; [[PHCachingImageManager defaultManager] requestImageDataForAsset:re.firstObject options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { UIImage * img = [UIImage imageWithData:imageData]; if (img) { if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { //将照片存入相册中 UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil); } [self pushSelectSinglePhotoViewControllerWithImage:img controller:picker]; } }]; } } } - (void)pushSelectSinglePhotoViewControllerWithImage:(UIImage *)image controller:(UIImagePickerController *)picker { SPSBSelectSinglePhotoViewController *vc = SPSBSelectSinglePhotoViewController.new; vc.spsb_needToDismiss = false; vc.spsb_dataImage = image; vc.spsb_proportion = SPSBIdentifierCardScale; @weakify(self) [vc passValue:^(NSValue * _Nonnull value) { @strongify(self) [vc.view showLoadingToastWithTitle:@"正在处理" userClick:false]; dispatch_async(dispatch_get_global_queue(0, 0), ^{ UIImage *_image = [UIImage fixOrientation:image]; CGImageRef imageRef = CGImageCreateWithImageInRect(_image.CGImage, value.CGRectValue); _image = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); _image = [self scaleImage:_image]; dispatch_async(dispatch_get_main_queue(), ^{ [vc.view dismissToast]; [self dismissViewControllerAnimated:true completion:^{ [self dismissViewControllerAnimated:true completion:nil]; if (self->_spsb_caughtImage) { self->_spsb_caughtImage(_image); } }]; }); }); }]; [picker pushViewController:vc animated:true]; } #pragma mark - 对焦 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.view]; [self setFocusWithPoint:point]; } - (void)setFocusWithPoint:(CGPoint)point { //将UI坐标转化为摄像头坐标 CGPoint cameraPoint= [_previewlayer captureDevicePointOfInterestForPoint:point]; [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; } if ([captureDevice isFocusPointOfInterestSupported]) { [captureDevice setFocusPointOfInterest:cameraPoint]; } }]; } - (void)changeDeviceProperty:(void(^)(AVCaptureDevice *captureDevice))propertyChange { AVCaptureDevice *captureDevice = [_dataInput device]; NSError *error; //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁 if ([captureDevice lockForConfiguration:&error]) { propertyChange(captureDevice); [captureDevice unlockForConfiguration]; } else{ debugLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription); } } #pragma mark - Network Action #pragma mark - UI - (void)setupUI { [self createTools]; } - (void)createTools { _imageView = [[UIImageView alloc] init]; _imageView.contentMode = UIViewContentModeScaleAspectFill; [self.view addSubview:_imageView]; [_imageView makeConstraints:^(JXHConstraintMaker *make) { make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0)); }]; CGFloat width = jxh_screenWidth() - 46 * 2; CGFloat height = width * SPSBIdentifierCardScale; CGFloat top = jxh_safeInsets(jxh_keyWindow()).bottom == 0 ? 50.f : 90.f; CGFloat bottom = jxh_safeInsets(jxh_keyWindow()).bottom == 0 ? 120.f : 158.f; CGFloat topInterval = (jxh_screenHeight() - top - bottom - jxh_safeInsets(jxh_keyWindow()).bottom - height) / 2; _topShadeViewHeight = top + topInterval; _leftShadViewWidth = 46.f; _bottomShadeViewHeight = bottom + jxh_safeInsets(jxh_keyWindow()).bottom + topInterval; UIView *topView = UIView.new; topView.backgroundColor = spsb_000000_color(1.f); [self.view addSubview:topView]; [topView makeConstraints:^(JXHConstraintMaker *make) { make.top.leading.and.trailing.equalTo(0); make.height.equalTo(top); }]; UIImageView *leftTop = [[UIImageView alloc] initWithImage:jxh_getImage(left_top)]; [self.view addSubview:leftTop]; [leftTop makeConstraints:^(JXHConstraintMaker *make) { make.leading.equalTo(self->_leftShadViewWidth); make.top.equalTo(self->_topShadeViewHeight); }]; UIImageView *rightTop = [[UIImageView alloc] initWithImage:jxh_getImage(right_top)]; [self.view addSubview:rightTop]; [rightTop makeConstraints:^(JXHConstraintMaker *make) { make.trailing.equalTo(-self->_leftShadViewWidth); make.top.equalTo(self->_topShadeViewHeight); }]; UIImageView *leftBottom = [[UIImageView alloc] initWithImage:jxh_getImage(left_bottom)]; [self.view addSubview:leftBottom]; [leftBottom makeConstraints:^(JXHConstraintMaker *make) { make.leading.equalTo(self->_leftShadViewWidth); make.bottom.equalTo(-self->_bottomShadeViewHeight); }]; UIImageView *rightBottom = [[UIImageView alloc] initWithImage:jxh_getImage(right_bottom)]; [self.view addSubview:rightBottom]; [rightBottom makeConstraints:^(JXHConstraintMaker *make) { make.trailing.equalTo(-self->_leftShadViewWidth); make.bottom.equalTo(-self->_bottomShadeViewHeight); }]; UIView *bottomView = UIView.new; bottomView.backgroundColor = spsb_000000_color(1.f); [self.view addSubview:bottomView]; [bottomView makeConstraints:^(JXHConstraintMaker *make) { make.bottom.and.leading.and.trailing.equalTo(0); make.top.equalTo(self.view.safebottom).offset(-bottom); }]; UIView *bottomTool = UIView.new; [bottomView addSubview:bottomTool]; [bottomTool makeConstraints:^(JXHConstraintMaker *make) { make.top.and.leading.and.trailing.equalTo(0); make.bottom.equalTo(bottomView.safebottom); }]; UIButton *takePhotoButton = [UIButton convenienceWithTarget:self action:@selector(takePhotoAction)]; [takePhotoButton setImage:jxh_getImage(photo_btn_n) state:JXHButtonControlStateNormal]; [bottomTool addSubview:takePhotoButton]; [takePhotoButton makeConstraints:^(JXHConstraintMaker *make) { make.center.equalTo(0); }]; UIButton *openPhotoAlbumButton = [UIButton convenienceWithFont:spsb_font(16) target:self action:@selector(openPhotoAlbum)]; [openPhotoAlbumButton setTitle:@"相册" titleColor:spsb_FFFFFF_color(1.f) state:JXHButtonControlStateNormal]; [bottomView addSubview:openPhotoAlbumButton]; [openPhotoAlbumButton makeConstraints:^(JXHConstraintMaker *make) { make.leading.equalTo(40); make.centerY.equalTo(takePhotoButton); }]; UIButton *cancelButton = [UIButton convenienceWithFont:spsb_font(16) target:self action:@selector(backAction)]; [cancelButton setTitle:@"取消" titleColor:spsb_FFFFFF_color(1.f) state:JXHButtonControlStateNormal]; [bottomView addSubview:cancelButton]; [cancelButton makeConstraints:^(JXHConstraintMaker *make) { make.trailing.equalTo(-40); make.centerY.equalTo(takePhotoButton); }]; _tipsLabel = [UILabel convenienceWithFont:spsb_font(16) text:@"请将身份证对准框内,建议横屏拍摄" textColor:spsb_FFFFFF_color(1.f) textAlignment:NSTextAlignmentCenter]; _tipsLabel.backgroundColor = spsb_000000_color(0.3); [_tipsLabel setLayerCornerRadius:22.5 clipToBounds:true]; [self.view addSubview:_tipsLabel]; [_tipsLabel makeConstraints:^(JXHConstraintMaker *make) { make.centerX.equalTo(self.view); make.centerY.equalTo(self.view.top).offset(self->_topShadeViewHeight + height / 2); make.size.equalTo(CGSizeMake(286, 45)); }]; } @end