SPSBFilmingIdentityCardViewController.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //
  2. // SPSBFilmingIdentityCardViewController.m
  3. // 我的社保
  4. //
  5. // Created by shanp on 2021/5/18.
  6. //
  7. #import "SPSBFilmingIdentityCardViewController.h"
  8. #import <AVFoundation/AVFoundation.h>
  9. #import <ImageIO/ImageIO.h>
  10. #import "SPSBUIGeneralHeader.h"
  11. #import <Photos/Photos.h>
  12. #import "UIView+SPSBLoadingTipsView.h"
  13. #import <UIImage+JXHProcessingImage.h>
  14. #import "SPSBConfirmAlertViewController.h"
  15. #import "SPSBSelectSinglePhotoViewController.h"
  16. @interface SPSBFilmingIdentityCardViewController ()<UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
  17. AVCaptureSession *_session;
  18. AVCaptureStillImageOutput *_dataOutput;
  19. AVCaptureDeviceInput * _dataInput;
  20. AVCaptureVideoPreviewLayer *_previewlayer;
  21. UIImageView *_imageView;
  22. CGFloat _topShadeViewHeight;
  23. CGFloat _leftShadViewWidth;
  24. CGFloat _bottomShadeViewHeight;
  25. bool _canOpenCamera;
  26. bool _alert;
  27. UIView *_tipsLabel;
  28. }
  29. @end
  30. @implementation SPSBFilmingIdentityCardViewController
  31. - (instancetype)init {
  32. self = [super init];
  33. if (!self) return nil;
  34. self.modalPresentationStyle = UIModalPresentationFullScreen;
  35. return self;
  36. }
  37. - (void)viewDidLoad {
  38. [super viewDidLoad];
  39. [self setUp];
  40. }
  41. - (void)viewDidAppear:(BOOL)animated {
  42. [super viewDidAppear:animated];
  43. if (_tipsLabel) {
  44. [UIView animateWithDuration:0.3 delay:2 options:0 animations:^{
  45. self->_tipsLabel.alpha = 0;
  46. } completion:^(BOOL finished) {
  47. [self->_tipsLabel removeFromSuperview];
  48. self->_tipsLabel = nil;
  49. }];
  50. }
  51. if (!_canOpenCamera) {
  52. if (_alert) {
  53. [self backAction];
  54. return;
  55. }
  56. _alert = true;
  57. SPSBConfirmAlertViewController *vc = [SPSBConfirmAlertViewController alertWithTitle:@"提示" content:@"不能打开相机,是否直接进入相册"];
  58. @weakify(self)
  59. [vc setCancelButtonTitle:@"暂时不进入" titleColor:nil action:^{
  60. @strongify(self)
  61. [self backAction];
  62. }];
  63. [vc setConfirmButtonTitle:@"立即进入" titleColor:nil action:^{
  64. @strongify(self)
  65. [self openPhotoAlbum];
  66. }];
  67. [self presentViewController:vc animated:false completion:nil];
  68. }
  69. }
  70. #pragma mark - Action
  71. - (void)setUp {
  72. //-- Setup Capture Session.
  73. _session = [[AVCaptureSession alloc] init];
  74. [_session beginConfiguration];
  75. //-- Set preset session size.
  76. if ([_session canSetSessionPreset:AVCaptureSessionPresetHigh]) {//设置分辨率
  77. _session.sessionPreset = AVCaptureSessionPresetHigh;
  78. }
  79. //-- Creata a video device and input from that Device. Add the input to the capture session.
  80. AVCaptureDevice * videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  81. NSError *error;
  82. _dataInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
  83. if (error) {
  84. _canOpenCamera = false;
  85. return;
  86. }
  87. _canOpenCamera = true;
  88. [_session addInput:_dataInput];
  89. _previewlayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session];
  90. _previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  91. _previewlayer.frame = self.view.bounds;
  92. [self.view.layer insertSublayer:_previewlayer atIndex:0];
  93. [self setupUI];
  94. //-- Create the output for the capture session.
  95. _dataOutput = [[AVCaptureStillImageOutput alloc] init];
  96. [_session addOutput:_dataOutput];
  97. [_session commitConfiguration];
  98. [_session startRunning];
  99. }
  100. - (void)takePhotoAction {
  101. AVCaptureConnection *videoConnection = nil;
  102. for (AVCaptureConnection *connection in _dataOutput.connections) {
  103. for (AVCaptureInputPort *port in [connection inputPorts]) {
  104. if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
  105. videoConnection = connection; //先找出目标connections(见图)
  106. break;
  107. }
  108. }
  109. if (videoConnection) { break; }
  110. }
  111. //锁定connection后就可以获取静态图了。
  112. @weakify(self)
  113. [_dataOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
  114. @strongify(self)
  115. if (imageDataSampleBuffer) {
  116. NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
  117. UIImage *image = [[UIImage alloc] initWithData:imageData];
  118. self->_imageView.image = image;
  119. [self handleImage:image];
  120. [self->_session stopRunning];
  121. } else {
  122. [self.view showToastWithTitle:@"获取图像失败请重试"];
  123. }
  124. }];
  125. }
  126. - (void)handleImage:(UIImage *)image {
  127. self.view.userInteractionEnabled = false;
  128. [self.view showLoadingToastWithTitle:@"正在处理" userClick:false];
  129. @weakify(self)
  130. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  131. @strongify(self)
  132. debugLog(@"%lf-----%lf", image.size.width, image.size.height);
  133. debugLog(@"%lf-----%lf", jxh_screenWidth(), jxh_screenHeight());
  134. UIImage *_image = [UIImage fixOrientation:image];
  135. CGFloat scale = _image.size.height / jxh_screenHeight();
  136. CGFloat x = (_image.size.width - _image.size.height * jxh_screenWidth() / jxh_screenHeight()) / 2 + self->_leftShadViewWidth * scale;
  137. CGFloat width = (jxh_screenWidth() - self->_leftShadViewWidth * 2) * scale;
  138. CGImageRef imageRef = CGImageCreateWithImageInRect(_image.CGImage, CGRectMake(x, self->_topShadeViewHeight * scale, width, width * SPSBIdentifierCardScale));
  139. _image = [UIImage imageWithCGImage:imageRef];
  140. CGImageRelease(imageRef);
  141. _image = [self changeTheDirectionOfTheImage:_image];
  142. _image = [self scaleImage:_image];
  143. @weakify(self)
  144. dispatch_async(dispatch_get_main_queue(), ^{
  145. [self.view dismissToast];
  146. [self dismissViewControllerAnimated:true completion:^{
  147. @strongify(self)
  148. if (self->_spsb_caughtImage) {
  149. self->_spsb_caughtImage(_image);
  150. }
  151. }];
  152. });
  153. });
  154. }
  155. - (UIImage *)scaleImage:(UIImage *)image {
  156. UIImage *img = [UIImage imageWithImage:image scale:480];
  157. debugLog(@"%lf-----%lf", img.size.width, img.size.height);
  158. return img;
  159. }
  160. - (UIImage *)changeTheDirectionOfTheImage:(UIImage *)image {
  161. CGFloat rotate = M_PI_2;
  162. CGRect rect = CGRectMake(0, 0, image.size.height, image.size.width);
  163. CGFloat translateX = 0;
  164. CGFloat translateY = -rect.size.width;
  165. CGFloat scaleY = rect.size.width/rect.size.height;
  166. CGFloat scaleX = rect.size.height/rect.size.width;
  167. UIGraphicsBeginImageContext(rect.size);
  168. CGContextRef context = UIGraphicsGetCurrentContext();
  169. //做CTM变换
  170. CGContextTranslateCTM(context, 0.0, rect.size.height);
  171. CGContextScaleCTM(context, 1.0, -1.0);
  172. CGContextRotateCTM(context, rotate);
  173. CGContextTranslateCTM(context, translateX, translateY);
  174. CGContextScaleCTM(context, scaleX, scaleY);
  175. //绘制图片
  176. CGContextDrawImage(context, CGRectMake(0, 0, rect.size.width, rect.size.height), image.CGImage);
  177. UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
  178. return img;
  179. }
  180. //打开相册
  181. - (void)openPhotoAlbum {
  182. //
  183. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
  184. UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
  185. imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
  186. imagePicker.delegate = self;
  187. imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  188. [imagePicker setEditing:true animated:true]; //允许编辑
  189. [self presentViewController:imagePicker animated:true completion:^{
  190. }];
  191. } else {
  192. [self.view showToastWithTitle:@"相册无法打开"];
  193. [self dismissViewControllerAnimated:true completion:nil];
  194. }
  195. }
  196. - (void)backAction {
  197. [self dismissViewControllerAnimated:true completion:nil];
  198. }
  199. #pragma mark - UIImagePickerControllerDelegate
  200. //打开相册之后选择的方法
  201. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  202. //获得媒体类型
  203. NSString *mediaType = [info valueForKey:UIImagePickerControllerMediaType];
  204. if ([mediaType hasSuffix:@"image"])
  205. {
  206. UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
  207. if (image) {
  208. [self pushSelectSinglePhotoViewControllerWithImage:image controller:picker];
  209. return;
  210. }
  211. NSURL *url = [info valueForKey:UIImagePickerControllerReferenceURL];
  212. NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString];
  213. __block NSString *localIdentifiers;
  214. [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  215. if ([obj.name isEqualToString:@"id"]) {
  216. localIdentifiers = obj.value;
  217. *stop = true;
  218. }
  219. }];
  220. if (localIdentifiers) {
  221. PHFetchResult * re = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifiers] options:nil];
  222. [[PHCachingImageManager defaultManager] requestImageDataForAsset:re.firstObject options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
  223. UIImage * img = [UIImage imageWithData:imageData];
  224. if (img) {
  225. if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
  226. //将照片存入相册中
  227. UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);
  228. }
  229. [self pushSelectSinglePhotoViewControllerWithImage:img controller:picker];
  230. }
  231. }];
  232. }
  233. }
  234. }
  235. - (void)pushSelectSinglePhotoViewControllerWithImage:(UIImage *)image controller:(UIImagePickerController *)picker {
  236. SPSBSelectSinglePhotoViewController *vc = SPSBSelectSinglePhotoViewController.new;
  237. vc.spsb_needToDismiss = false;
  238. vc.spsb_dataImage = image;
  239. vc.spsb_proportion = SPSBIdentifierCardScale;
  240. @weakify(self)
  241. [vc passValue:^(NSValue * _Nonnull value) {
  242. @strongify(self)
  243. [vc.view showLoadingToastWithTitle:@"正在处理" userClick:false];
  244. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  245. UIImage *_image = [UIImage fixOrientation:image];
  246. CGImageRef imageRef = CGImageCreateWithImageInRect(_image.CGImage, value.CGRectValue);
  247. _image = [UIImage imageWithCGImage:imageRef];
  248. CGImageRelease(imageRef);
  249. _image = [self scaleImage:_image];
  250. dispatch_async(dispatch_get_main_queue(), ^{
  251. [vc.view dismissToast];
  252. [self dismissViewControllerAnimated:true completion:^{
  253. [self dismissViewControllerAnimated:true completion:nil];
  254. if (self->_spsb_caughtImage) {
  255. self->_spsb_caughtImage(_image);
  256. }
  257. }];
  258. });
  259. });
  260. }];
  261. [picker pushViewController:vc animated:true];
  262. }
  263. #pragma mark - 对焦
  264. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  265. UITouch *touch = [touches anyObject];
  266. CGPoint point = [touch locationInView:self.view];
  267. [self setFocusWithPoint:point];
  268. }
  269. - (void)setFocusWithPoint:(CGPoint)point {
  270. //将UI坐标转化为摄像头坐标
  271. CGPoint cameraPoint= [_previewlayer captureDevicePointOfInterestForPoint:point];
  272. [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  273. if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
  274. [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
  275. }
  276. if ([captureDevice isFocusPointOfInterestSupported]) {
  277. [captureDevice setFocusPointOfInterest:cameraPoint];
  278. }
  279. }];
  280. }
  281. - (void)changeDeviceProperty:(void(^)(AVCaptureDevice *captureDevice))propertyChange {
  282. AVCaptureDevice *captureDevice = [_dataInput device];
  283. NSError *error;
  284. //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
  285. if ([captureDevice lockForConfiguration:&error]) {
  286. propertyChange(captureDevice);
  287. [captureDevice unlockForConfiguration];
  288. } else{
  289. debugLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
  290. }
  291. }
  292. #pragma mark - Network Action
  293. #pragma mark - UI
  294. - (void)setupUI {
  295. [self createTools];
  296. }
  297. - (void)createTools {
  298. _imageView = [[UIImageView alloc] init];
  299. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  300. [self.view addSubview:_imageView];
  301. [_imageView makeConstraints:^(JXHConstraintMaker *make) {
  302. make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
  303. }];
  304. CGFloat width = jxh_screenWidth() - 46 * 2;
  305. CGFloat height = width * SPSBIdentifierCardScale;
  306. CGFloat top = jxh_safeInsets(jxh_keyWindow()).bottom == 0 ? 50.f : 90.f;
  307. CGFloat bottom = jxh_safeInsets(jxh_keyWindow()).bottom == 0 ? 120.f : 158.f;
  308. CGFloat topInterval = (jxh_screenHeight() - top - bottom - jxh_safeInsets(jxh_keyWindow()).bottom - height) / 2;
  309. _topShadeViewHeight = top + topInterval;
  310. _leftShadViewWidth = 46.f;
  311. _bottomShadeViewHeight = bottom + jxh_safeInsets(jxh_keyWindow()).bottom + topInterval;
  312. UIView *topView = UIView.new;
  313. topView.backgroundColor = spsb_000000_color(1.f);
  314. [self.view addSubview:topView];
  315. [topView makeConstraints:^(JXHConstraintMaker *make) {
  316. make.top.leading.and.trailing.equalTo(0);
  317. make.height.equalTo(top);
  318. }];
  319. UIImageView *leftTop = [[UIImageView alloc] initWithImage:jxh_getImage(left_top)];
  320. [self.view addSubview:leftTop];
  321. [leftTop makeConstraints:^(JXHConstraintMaker *make) {
  322. make.leading.equalTo(self->_leftShadViewWidth);
  323. make.top.equalTo(self->_topShadeViewHeight);
  324. }];
  325. UIImageView *rightTop = [[UIImageView alloc] initWithImage:jxh_getImage(right_top)];
  326. [self.view addSubview:rightTop];
  327. [rightTop makeConstraints:^(JXHConstraintMaker *make) {
  328. make.trailing.equalTo(-self->_leftShadViewWidth);
  329. make.top.equalTo(self->_topShadeViewHeight);
  330. }];
  331. UIImageView *leftBottom = [[UIImageView alloc] initWithImage:jxh_getImage(left_bottom)];
  332. [self.view addSubview:leftBottom];
  333. [leftBottom makeConstraints:^(JXHConstraintMaker *make) {
  334. make.leading.equalTo(self->_leftShadViewWidth);
  335. make.bottom.equalTo(-self->_bottomShadeViewHeight);
  336. }];
  337. UIImageView *rightBottom = [[UIImageView alloc] initWithImage:jxh_getImage(right_bottom)];
  338. [self.view addSubview:rightBottom];
  339. [rightBottom makeConstraints:^(JXHConstraintMaker *make) {
  340. make.trailing.equalTo(-self->_leftShadViewWidth);
  341. make.bottom.equalTo(-self->_bottomShadeViewHeight);
  342. }];
  343. UIView *bottomView = UIView.new;
  344. bottomView.backgroundColor = spsb_000000_color(1.f);
  345. [self.view addSubview:bottomView];
  346. [bottomView makeConstraints:^(JXHConstraintMaker *make) {
  347. make.bottom.and.leading.and.trailing.equalTo(0);
  348. make.top.equalTo(self.view.safebottom).offset(-bottom);
  349. }];
  350. UIView *bottomTool = UIView.new;
  351. [bottomView addSubview:bottomTool];
  352. [bottomTool makeConstraints:^(JXHConstraintMaker *make) {
  353. make.top.and.leading.and.trailing.equalTo(0);
  354. make.bottom.equalTo(bottomView.safebottom);
  355. }];
  356. UIButton *takePhotoButton = [UIButton convenienceWithTarget:self action:@selector(takePhotoAction)];
  357. [takePhotoButton setImage:jxh_getImage(photo_btn_n) state:JXHButtonControlStateNormal];
  358. [bottomTool addSubview:takePhotoButton];
  359. [takePhotoButton makeConstraints:^(JXHConstraintMaker *make) {
  360. make.center.equalTo(0);
  361. }];
  362. UIButton *openPhotoAlbumButton = [UIButton convenienceWithFont:spsb_font(16) target:self action:@selector(openPhotoAlbum)];
  363. [openPhotoAlbumButton setTitle:@"相册" titleColor:spsb_FFFFFF_color(1.f) state:JXHButtonControlStateNormal];
  364. [bottomView addSubview:openPhotoAlbumButton];
  365. [openPhotoAlbumButton makeConstraints:^(JXHConstraintMaker *make) {
  366. make.leading.equalTo(40);
  367. make.centerY.equalTo(takePhotoButton);
  368. }];
  369. UIButton *cancelButton = [UIButton convenienceWithFont:spsb_font(16) target:self action:@selector(backAction)];
  370. [cancelButton setTitle:@"取消" titleColor:spsb_FFFFFF_color(1.f) state:JXHButtonControlStateNormal];
  371. [bottomView addSubview:cancelButton];
  372. [cancelButton makeConstraints:^(JXHConstraintMaker *make) {
  373. make.trailing.equalTo(-40);
  374. make.centerY.equalTo(takePhotoButton);
  375. }];
  376. _tipsLabel = [UILabel convenienceWithFont:spsb_font(16) text:@"请将身份证对准框内,建议横屏拍摄" textColor:spsb_FFFFFF_color(1.f) textAlignment:NSTextAlignmentCenter];
  377. _tipsLabel.backgroundColor = spsb_000000_color(0.3);
  378. [_tipsLabel setLayerCornerRadius:22.5 clipToBounds:true];
  379. [self.view addSubview:_tipsLabel];
  380. [_tipsLabel makeConstraints:^(JXHConstraintMaker *make) {
  381. make.centerX.equalTo(self.view);
  382. make.centerY.equalTo(self.view.top).offset(self->_topShadeViewHeight + height / 2);
  383. make.size.equalTo(CGSizeMake(286, 45));
  384. }];
  385. }
  386. @end