RACameraViewController.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. //
  2. // RACameraViewController.m
  3. // Apex And Drivers
  4. //
  5. // Created by Jack on 2018/6/5.
  6. // Copyright © 2018年 USAI. All rights reserved.
  7. //
  8. #import "RACameraViewController.h"
  9. #import <AVFoundation/AVFoundation.h>
  10. #import "RATakePhotoPreviewController.h"
  11. #import <CoreMotion/CoreMotion.h>
  12. @interface RACameraViewController ()
  13. @property (nonatomic,strong) IBOutlet UIView *previewContainer;
  14. @property (nonatomic,strong) IBOutlet UIView *controlContanier;
  15. @property (strong, nonatomic) IBOutlet UIButton *takePhotoBtn;
  16. @property (strong, nonatomic) IBOutlet UIButton *backBtn;
  17. @property (nonatomic,assign) BOOL barHidden;
  18. #pragma mark - Camera
  19. @property (nonatomic,strong) AVCaptureDevice *captureDevice;
  20. @property (nonatomic,strong) AVCaptureSession *captureSession;
  21. @property (nonatomic,strong) AVCaptureDeviceInput *captureInput;
  22. @property (nonatomic,strong) AVCaptureStillImageOutput *captureOutput;
  23. @property (nonatomic,strong) AVCaptureVideoPreviewLayer *previewLayer;
  24. @property (nonatomic,assign) BOOL cameraInitial;
  25. @property (nonatomic,assign) AVLayerVideoGravity gravity;
  26. @property (strong, nonatomic) CMMotionManager *motionManager;
  27. @property (assign, nonatomic) UIDeviceOrientation orientationLast;
  28. @end
  29. @implementation RACameraViewController
  30. -(BOOL)shouldAutorotate {
  31. return NO;
  32. }
  33. - (void)initMotionManager
  34. {
  35. if (_motionManager == nil) {
  36. _motionManager = [[CMMotionManager alloc] init];
  37. }
  38. // 提供设备运动数据到指定的时间间隔
  39. _motionManager.deviceMotionUpdateInterval = .3;
  40. if (_motionManager.deviceMotionAvailable) { // 确定是否使用任何可用的态度参考帧来决定设备的运动是否可用
  41. [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
  42. withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
  43. if (!error) {
  44. [self outputAccelertionData:accelerometerData.acceleration];
  45. }
  46. else{
  47. NSLog(@"%@", error);
  48. }
  49. }];
  50. }else
  51. {
  52. [self setMotionManager:nil];
  53. }
  54. }
  55. - (void)outputAccelertionData:(CMAcceleration)acceleration{
  56. UIDeviceOrientation orientationNew;
  57. // NSLog(@"x %f y %f z %f",acceleration.x,acceleration.y, acceleration.z);
  58. if (acceleration.x >= 0.75) {
  59. orientationNew = UIDeviceOrientationLandscapeRight;
  60. }
  61. else if (acceleration.x <= -0.75) {
  62. orientationNew = UIDeviceOrientationLandscapeLeft;
  63. }
  64. else if (acceleration.y <= -0.75) {
  65. orientationNew = UIDeviceOrientationPortrait;
  66. }
  67. else if (acceleration.y >= 0.75) {
  68. orientationNew = UIDeviceOrientationPortraitUpsideDown;
  69. }
  70. else {
  71. // Consider same as last time
  72. return;
  73. }
  74. if (orientationNew == self.orientationLast)
  75. return;
  76. self.orientationLast = orientationNew;
  77. // [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientationNew] forKey:@"orientation"];
  78. // CGRect frame = self.previewContainer.bounds;
  79. // [self resetPreview:frame];
  80. }
  81. + (NSString *)storyboardID {
  82. return NSStringFromClass([self class]);
  83. }
  84. + (instancetype)viewControllerFromStoryboard {
  85. RACameraViewController *cameraVC = [[UIStoryboard storyboardWithName:@"Camera" bundle:nil] instantiateViewControllerWithIdentifier:[self storyboardID]];
  86. cameraVC.takeMode = RACameraTakeModeTakeOnce;
  87. cameraVC.gravity = AVLayerVideoGravityResizeAspectFill;
  88. return cameraVC;
  89. }
  90. + (instancetype)showCameraFromViewController:(UIViewController *)from withTakeMode:(RACameraTakeMode)mode videoGravity:(AVLayerVideoGravity)gravity completion:(void(^)(UIImage *image))completion {
  91. RACameraViewController *vc = [self viewControllerFromStoryboard];
  92. vc.takeMode = mode;
  93. if (!gravity) {
  94. gravity = AVLayerVideoGravityResizeAspectFill;
  95. }
  96. vc.gravity = gravity;
  97. vc.completion = completion;
  98. return vc;
  99. }
  100. - (void)viewDidLoad {
  101. [super viewDidLoad];
  102. // Do any additional setup after loading the view.
  103. [self initMotionManager];
  104. UIImage *normal_img = [self.class imageWithColor:[UIColor redColor] Size:CGSizeMake(60, 60)];
  105. UIImage *highlight_img = [self.class imageWithColor:[UIColor whiteColor] Size:CGSizeMake(60, 60)];
  106. [self.takePhotoBtn setImage:normal_img forState:UIControlStateNormal];
  107. [self.takePhotoBtn setImage:highlight_img forState:UIControlStateHighlighted];
  108. // [self initCapture];
  109. // [self initTakePicture];
  110. if ([self camerAuthorization]) {
  111. [self initCapture];
  112. [self initTakePicture];
  113. } else {
  114. // fix:第一次打开相机的时候授权成功后,未初始化相机
  115. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  116. dispatch_async(dispatch_get_main_queue(), ^{
  117. if (granted) {
  118. [self initCapture];
  119. [self initTakePicture];
  120. CGRect frame = self.previewContainer.bounds;
  121. [self resetPreview:CGRectOffset(frame, CGRectGetWidth(frame), 0)];
  122. [UIView animateWithDuration:0.3 animations:^{
  123. self.previewLayer.frame = frame;
  124. }];
  125. if (self.cameraInitial && !self.captureSession.isRunning) {
  126. [self.captureSession startRunning];
  127. }
  128. } else {
  129. NSDictionary* infoDict =[[NSBundle mainBundle] infoDictionary];
  130. NSString *appName = [infoDict objectForKey:@"CFBundleDisplayName"];
  131. if (!appName) {
  132. appName = [infoDict objectForKey:@"CFBundleName"];
  133. }
  134. __weak typeof(self) weakSelf = self;
  135. UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:[NSString stringWithFormat:@"Camera access denied, please change %@ setting, allow App use camera. (setting -> privacy -> camera enable %@)",[UIDevice currentDevice].model,appName] preferredStyle:UIAlertControllerStyleAlert];
  136. UIAlertAction *action = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  137. [weakSelf dismissViewControllerAnimated:YES completion:nil];
  138. }];
  139. [alert addAction:action];
  140. [self presentViewController:alert animated:YES completion:nil];
  141. }
  142. });
  143. }];
  144. }
  145. /**
  146. * 管理导航条
  147. * 在viewDidload的时候隐藏导航条
  148. * 恢复导航条的显示/隐藏有两个地方:1.退出的时候;2.确认使用照片的时候
  149. */
  150. if (self.navigationController) {
  151. BOOL navBarHidden = self.navigationController.navigationBar.hidden;
  152. self.barHidden = navBarHidden;
  153. if (!navBarHidden) {
  154. [self.navigationController setNavigationBarHidden:YES animated:YES];
  155. }
  156. }
  157. }
  158. - (void)didReceiveMemoryWarning {
  159. [super didReceiveMemoryWarning];
  160. // Dispose of any resources that can be recreated.
  161. }
  162. - (void)viewWillAppear:(BOOL)animated {
  163. [super viewWillAppear:animated];
  164. if (self.cameraInitial && !self.captureSession.isRunning) {
  165. [self.captureSession startRunning];
  166. }
  167. }
  168. - (void)viewWillDisappear:(BOOL)animated {
  169. [super viewWillDisappear:animated];
  170. if ([self.captureSession isRunning]) {
  171. [self.captureSession stopRunning];
  172. }
  173. }
  174. - (BOOL)prefersStatusBarHidden {
  175. return YES;
  176. }
  177. - (void)viewDidLayoutSubviews {
  178. [super viewDidLayoutSubviews];
  179. // 强制布局一次,不然 takePhotoBtn frame还没发生改变
  180. [self.controlContanier layoutIfNeeded];
  181. if ([self camerAuthorization] && self.cameraInitial) {
  182. CGRect frame = self.previewContainer.bounds;
  183. BOOL isPortrait = self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown;
  184. // aspect ratio 3:4
  185. if (self.gravity == AVLayerVideoGravityResizeAspect) {
  186. CGRect backBtnFrame = [self.previewContainer convertRect:self.backBtn.frame fromView:self.controlContanier];
  187. CGRect captureBtnFrame = [self.previewContainer convertRect:self.takePhotoBtn.frame fromView:self.controlContanier];
  188. if (isPortrait) {
  189. CGFloat left = 0;
  190. CGFloat top = CGRectGetMaxY(backBtnFrame);
  191. CGFloat bottom = CGRectGetMinY(captureBtnFrame);
  192. CGFloat width = CGRectGetWidth(self.controlContanier.bounds);
  193. CGFloat height = bottom - top;
  194. frame = CGRectMake(left, top, width, height);
  195. } else {
  196. CGFloat left = CGRectGetMaxX(backBtnFrame);
  197. CGFloat right = CGRectGetMinX(captureBtnFrame);
  198. CGFloat top = 0;
  199. CGFloat width = right - left;
  200. CGFloat bottom = CGRectGetHeight(self.controlContanier.bounds);
  201. CGFloat height = bottom - top;
  202. frame = CGRectMake(left, top, width, height);
  203. }
  204. }
  205. [self resetPreview:frame];
  206. }
  207. }
  208. #pragma mark - Capture
  209. - (void)resetPreview:(CGRect)frame {
  210. self.previewLayer.frame = frame;
  211. // if (self.previewLayer.connection.isVideoOrientationSupported) {
  212. // self.previewLayer.connection.videoOrientation = [self captureVideoOrientation];
  213. // }
  214. //
  215. // if ([self.captureOutput connectionWithMediaType:AVMediaTypeVideo].isVideoOrientationSupported) {
  216. // [self.captureOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = [self captureVideoOrientation];
  217. // }
  218. //
  219. }
  220. - (BOOL)camerAuthorization {
  221. AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  222. if (status == AVAuthorizationStatusRestricted || status == AVAuthorizationStatusDenied) {
  223. return NO;
  224. }
  225. return YES;
  226. }
  227. - (void)initCapture {
  228. self.cameraInitial = NO;
  229. if (![self camerAuthorization]) {
  230. NSDictionary* infoDict =[[NSBundle mainBundle] infoDictionary];
  231. NSString *appName = [infoDict objectForKey:@"CFBundleDisplayName"];
  232. if (!appName) {
  233. appName = [infoDict objectForKey:@"CFBundleName"];
  234. }
  235. __weak typeof(self) weakSelf = self;
  236. UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:[NSString stringWithFormat:@"Camera access denied, please change %@ setting, allow App use camera. (setting -> privacy -> camera enable %@)",[UIDevice currentDevice].model,appName] preferredStyle:UIAlertControllerStyleAlert];
  237. UIAlertAction *action = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  238. [weakSelf dismissViewControllerAnimated:YES completion:nil];
  239. }];
  240. [alert addAction:action];
  241. [self presentViewController:alert animated:YES completion:nil];
  242. return;
  243. }
  244. self.captureSession = [[AVCaptureSession alloc] init];
  245. self.captureDevice = [self videoDevicePosition:AVCaptureDevicePositionBack];
  246. if (!self.captureDevice) {
  247. NSLog(@"there is no capture device while init camera");
  248. return;
  249. }
  250. NSError *error;
  251. self.captureInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.captureDevice error:&error];
  252. if (error) {
  253. NSLog(@"init camera error: %@",error);
  254. return;
  255. }
  256. [self.captureSession beginConfiguration];
  257. if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
  258. _captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
  259. }
  260. if ([self.captureDevice isFlashAvailable] && [_captureDevice isFlashModeSupported:AVCaptureFlashModeAuto]) {
  261. NSError *configErr;
  262. [self.captureDevice lockForConfiguration:&configErr];
  263. [self.captureDevice setFlashMode:AVCaptureFlashModeAuto];
  264. [self.captureDevice unlockForConfiguration];
  265. }
  266. // if ([self.captureDevice isAdjustingWhiteBalance] && [self.captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
  267. // NSError *configErr;
  268. // [self.captureDevice lockForConfiguration:&configErr];
  269. // [self.captureDevice setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
  270. // [self.captureDevice unlockForConfiguration];
  271. // }
  272. if ([self.captureSession canAddInput:self.captureInput]) {
  273. [self.captureSession addInput:self.captureInput];
  274. // if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
  275. // self.captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
  276. // } else {
  277. // if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
  278. // self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
  279. // }
  280. // }
  281. } else {
  282. NSLog(@"init camera can't add input");
  283. return;
  284. }
  285. [self.captureSession commitConfiguration];
  286. self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
  287. self.previewLayer.videoGravity = self.gravity;
  288. [self.previewContainer.layer addSublayer:self.previewLayer];
  289. self.cameraInitial = YES;
  290. }
  291. - (void)initTakePicture {
  292. if (!self.cameraInitial) {
  293. return;
  294. }
  295. self.captureOutput = [[AVCaptureStillImageOutput alloc] init];
  296. NSDictionary *setting = @{AVVideoCodecKey:AVVideoCodecJPEG};
  297. [self.captureOutput setOutputSettings:setting];
  298. if ([self.captureSession canAddOutput:self.captureOutput]) {
  299. [self.captureSession addOutput:self.captureOutput];
  300. }
  301. // AVCaptureConnection *connection = [self.captureOutput connectionWithMediaType:AVMediaTypeVideo];
  302. // if (connection.isVideoOrientationSupported) { // addOutput之后判断,否则一直都是false
  303. // connection.videoOrientation = [self captureVideoOrientation];
  304. // }
  305. }
  306. - (AVCaptureDevice *) videoDevicePosition:(AVCaptureDevicePosition)position {
  307. NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  308. for (AVCaptureDevice *device in videoDevices) {
  309. if ([device position] == position) {
  310. return device;
  311. }
  312. }
  313. return nil;
  314. }
  315. - (AVCaptureVideoOrientation)captureVideoOrientation {
  316. AVCaptureVideoOrientation result;
  317. // UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
  318. switch (self.orientationLast) {
  319. case UIDeviceOrientationPortrait:
  320. case UIDeviceOrientationFaceUp:
  321. case UIDeviceOrientationFaceDown:
  322. result = AVCaptureVideoOrientationPortrait;
  323. break;
  324. case UIDeviceOrientationPortraitUpsideDown:
  325. //如果这里设置成AVCaptureVideoOrientationPortraitUpsideDown,则视频方向和拍摄时的方向是相反的。
  326. result = AVCaptureVideoOrientationPortraitUpsideDown;
  327. break;
  328. case UIDeviceOrientationLandscapeLeft:
  329. result = AVCaptureVideoOrientationLandscapeRight;
  330. break;
  331. case UIDeviceOrientationLandscapeRight:
  332. result = AVCaptureVideoOrientationLandscapeLeft;
  333. break;
  334. default:
  335. result = AVCaptureVideoOrientationPortrait;
  336. break;
  337. }
  338. return result;
  339. }
  340. #pragma mark - Action
  341. - (IBAction)takePictureBtnClick:(UIButton *)sender {
  342. // UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
  343. // NSLog(@"UIDeviceOrientation %ld",deviceOrientation);
  344. // [self.captureOutput connectionWithMediaType:AVMediaTypeVideo];
  345. if (!self.cameraInitial) {
  346. return;
  347. }
  348. sender.enabled = false;
  349. AVCaptureConnection *connection = [self.captureOutput connectionWithMediaType:AVMediaTypeVideo];
  350. if (connection.isVideoOrientationSupported) { // addOutput之后判断,否则一直都是false
  351. connection.videoOrientation = [self captureVideoOrientation];
  352. }
  353. // NSLog(@"capture orientation %ld",connection.videoOrientation);
  354. ;
  355. __weak typeof(self) weakSelf = self;
  356. [self.captureOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
  357. if (imageDataSampleBuffer) {
  358. NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
  359. UIImage *image=[UIImage imageWithData:imageData];
  360. RATakePhotoPreviewController *preVC = [RATakePhotoPreviewController viewControllerFromStoryboard];
  361. preVC.photoHandler = ^(UIImage *img){
  362. if (weakSelf) {
  363. __strong typeof(weakSelf) strongSelf = weakSelf;
  364. if (strongSelf.completion) {
  365. strongSelf.completion(img);
  366. }
  367. }
  368. };
  369. preVC.preImage = image;
  370. if (weakSelf.takeMode == RACameraTakeModeTakeOnce) {
  371. preVC.popTo = weakSelf.fromVC;
  372. preVC.barHidden = weakSelf.barHidden;
  373. }
  374. [weakSelf.navigationController pushViewController:preVC animated:YES];
  375. } else {
  376. NSLog(@"take picture failed: %@",error);
  377. }
  378. sender.enabled = true;
  379. }];
  380. }
  381. - (IBAction)returnButtonClick:(UIButton *)sender {
  382. if (self.navigationController) {
  383. [self.navigationController popViewControllerAnimated:YES];
  384. } else {
  385. [self dismissViewControllerAnimated:YES completion:nil];
  386. }
  387. if (self.navigationController) {
  388. [self.navigationController setNavigationBarHidden:self.barHidden animated:YES];
  389. }
  390. }
  391. #pragma mark - Utils
  392. + (UIImage *)imageWithColor:(UIColor *)color Size:(CGSize)size {
  393. UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
  394. CGContextRef ctx = UIGraphicsGetCurrentContext();
  395. CGContextAddEllipseInRect(ctx, CGRectMake(5, 5, size.width - 10, size.height - 10));
  396. CGContextSetFillColorWithColor(ctx, color.CGColor);
  397. CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
  398. CGContextSetLineWidth(ctx, 3.0f);
  399. CGContextDrawPath(ctx, kCGPathFillStroke);
  400. UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
  401. UIGraphicsEndImageContext();
  402. return img;
  403. }
  404. @end