SLShotViewController.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. //
  2. // SLShotViewController.m
  3. // DarkMode
  4. //
  5. // Created by wsl on 2019/9/18.
  6. // Copyright © 2019 wsl. All rights reserved.
  7. //
  8. #import "SLShotViewController.h"
  9. #import "SLBlurView.h"
  10. #import "SLAvCaptureTool.h"
  11. #import "SLShotFocusView.h"
  12. #import "SLEditVideoController.h"
  13. #import "SLEditImageController.h"
  14. #define KMaxDurationOfVideo 4.0 //录制最大时长 s
  15. @interface SLShotViewController ()<SLAvCaptureToolDelegate>
  16. {
  17. dispatch_source_t _gcdTimer; //计时器
  18. NSTimeInterval _durationOfVideo; //录制视频的时长
  19. }
  20. @property (nonatomic, strong) SLAvCaptureTool *avCaptureTool; //摄像头采集工具
  21. @property (nonatomic, strong) UIImageView *captureView; // 捕获预览视图
  22. @property (nonatomic, strong) UIButton *switchCameraBtn; // 切换前后摄像头
  23. @property (nonatomic, strong) UIButton *backBtn;
  24. @property (nonatomic, strong) SLBlurView *shotBtn; //拍摄按钮
  25. @property (nonatomic, strong) UIView *whiteView; //白色圆心
  26. @property (nonatomic, strong) CAShapeLayer *progressLayer; //环形进度条
  27. @property (nonatomic, strong) UILabel *tipsLabel; //拍摄提示语 轻触拍照 长按拍摄
  28. @property (nonatomic, assign) CGFloat currentZoomFactor; //当前焦距比例系数
  29. @property (nonatomic, strong) SLShotFocusView *focusView; //当前聚焦视图
  30. @end
  31. @implementation SLShotViewController
  32. #pragma mark - OverWrite
  33. - (void)viewDidLoad {
  34. [super viewDidLoad];
  35. [self setupUI];
  36. }
  37. - (void)viewWillAppear:(BOOL)animated {
  38. [super viewWillAppear:animated];
  39. if (_isCheckBody) {
  40. [self.avCaptureTool switchsCamera:AVCaptureDevicePositionFront];
  41. }
  42. [self.avCaptureTool startRunning];
  43. [self focusAtPoint:CGPointMake(RQ_SCREEN_WIDTH/2.0, RQ_SCREEN_HEIGHT/2.0)];
  44. //监听设备方向,旋转切换摄像头按钮
  45. [self.avCaptureTool addObserver:self forKeyPath:@"shootingOrientation" options:NSKeyValueObservingOptionNew context:nil];
  46. }
  47. - (void)viewDidDisappear:(BOOL)animated {
  48. [super viewDidDisappear:animated];
  49. if (_gcdTimer) {
  50. dispatch_source_cancel(_gcdTimer);
  51. _gcdTimer = nil;
  52. }
  53. [_avCaptureTool stopRunning];
  54. [_avCaptureTool removeObserver:self forKeyPath:@"shootingOrientation"];
  55. [SLDelayPerform sl_cancelDelayPerform];
  56. }
  57. //- (void)viewSafeAreaInsetsDidChange {
  58. // [super viewSafeAreaInsetsDidChange];
  59. // UIEdgeInsets insets = self.view.safeAreaInsets;
  60. //}
  61. - (BOOL)prefersStatusBarHidden {
  62. return YES;
  63. }
  64. - (BOOL)shouldAutorotate {
  65. return NO;
  66. }
  67. - (void)dealloc {
  68. _avCaptureTool.delegate = nil;
  69. _avCaptureTool = nil;
  70. NSLog(@"拍摄视图释放");
  71. }
  72. #pragma mark - UI
  73. - (void)setupUI {
  74. self.title = @"拍摄";
  75. self.view.backgroundColor = [UIColor whiteColor];
  76. [self.view addSubview:self.captureView];
  77. [self.view addSubview:self.backBtn];
  78. [self.view addSubview:self.shotBtn];
  79. if (!_isCheckBody) {
  80. [self.view addSubview:self.switchCameraBtn];
  81. }
  82. [self.view addSubview:self.tipsLabel];
  83. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  84. [self.tipsLabel removeFromSuperview];
  85. });
  86. }
  87. #pragma mark - Getter
  88. - (SLAvCaptureTool *)avCaptureTool {
  89. if (_avCaptureTool == nil) {
  90. _avCaptureTool = [[SLAvCaptureTool alloc] init];
  91. _avCaptureTool.preview = self.captureView;
  92. _avCaptureTool.delegate = self;
  93. _avCaptureTool.videoSize = CGSizeMake(RQ_SCREEN_WIDTH*0.8, RQ_SCREEN_HEIGHT*0.8);
  94. }
  95. return _avCaptureTool;
  96. }
  97. - (UIView *)captureView {
  98. if (_captureView == nil) {
  99. _captureView = [[UIImageView alloc] initWithFrame:self.view.bounds];
  100. _captureView.contentMode = UIViewContentModeScaleAspectFit;
  101. _captureView.backgroundColor = [UIColor blackColor];
  102. _captureView.userInteractionEnabled = YES;
  103. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapFocusing:)];
  104. [_captureView addGestureRecognizer:tap];
  105. UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchFocalLength:)];
  106. [_captureView addGestureRecognizer:pinch];
  107. }
  108. return _captureView;
  109. }
  110. - (UIButton *)backBtn {
  111. if (_backBtn == nil) {
  112. _backBtn = [[UIButton alloc] init];
  113. _backBtn.frame = CGRectMake(0, 0, 30, 30);
  114. _backBtn.center = CGPointMake((self.view.sl_width/2 - 70/2.0)/2.0, self.view.sl_height - 80);
  115. [_backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
  116. [_backBtn addTarget:self action:@selector(backBtn:) forControlEvents:UIControlEventTouchUpInside];
  117. }
  118. return _backBtn;
  119. }
  120. - (UIView *)shotBtn {
  121. if (_shotBtn == nil) {
  122. _shotBtn = [[SLBlurView alloc] init];
  123. _shotBtn.userInteractionEnabled = YES;
  124. _shotBtn.frame = CGRectMake(0, 0, 70, 70);
  125. _shotBtn.center = CGPointMake(self.view.sl_width/2.0, self.view.sl_height - 80);
  126. _shotBtn.clipsToBounds = YES;
  127. _shotBtn.layer.cornerRadius = _shotBtn.sl_width/2.0;
  128. //轻触拍照,长按摄像
  129. UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(takePicture:)];
  130. [_shotBtn addGestureRecognizer:tap];
  131. // UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(recordVideo:)];
  132. // longPress.minimumPressDuration = 0.3;
  133. // [_shotBtn addGestureRecognizer:longPress];
  134. //中心白色
  135. self.whiteView.frame = CGRectMake(0, 0, 50, 50);
  136. self.whiteView.center = CGPointMake(_shotBtn.sl_width/2.0, _shotBtn.sl_height/2.0);
  137. self.whiteView.layer.cornerRadius = self.whiteView.frame.size.width/2.0;
  138. [_shotBtn addSubview:self.whiteView];
  139. }
  140. return _shotBtn;
  141. }
  142. - (UIButton *)switchCameraBtn {
  143. if (_switchCameraBtn == nil) {
  144. _switchCameraBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.sl_width - 30 - 30, 44 , 30, 30)];
  145. [_switchCameraBtn setImage:[UIImage imageNamed:@"cameraAround"] forState:UIControlStateNormal];
  146. [_switchCameraBtn addTarget:self action:@selector(switchCameraClicked:) forControlEvents:UIControlEventTouchUpInside];
  147. }
  148. return _switchCameraBtn;
  149. }
  150. - (UIView *)whiteView {
  151. if (_whiteView == nil) {
  152. _whiteView = [UIView new];
  153. _whiteView.backgroundColor = [UIColor whiteColor];
  154. }
  155. return _whiteView;
  156. }
  157. - (CAShapeLayer *)progressLayer {
  158. if (_progressLayer == nil) {
  159. //设置画笔路径
  160. UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.shotBtn.frame.size.width/2.0, self.shotBtn.frame.size.height/2.0) radius:self.shotBtn.frame.size.width/2.0 startAngle:- M_PI_2 endAngle:-M_PI_2 + M_PI * 2 clockwise:YES];
  161. //按照路径绘制圆环
  162. _progressLayer = [CAShapeLayer layer];
  163. _progressLayer.frame = _shotBtn.bounds;
  164. _progressLayer.fillColor = [UIColor clearColor].CGColor;
  165. _progressLayer.lineWidth = 10;
  166. //线头的样式
  167. _progressLayer.lineCap = kCALineCapButt;
  168. //圆环颜色
  169. _progressLayer.strokeColor = [UIColor colorWithRed:45/255.0 green:175/255.0 blue:45/255.0 alpha:1].CGColor;
  170. _progressLayer.strokeStart = 0;
  171. _progressLayer.strokeEnd = 0;
  172. //path 决定layer将被渲染成何种形状
  173. _progressLayer.path = path.CGPath;
  174. }
  175. return _progressLayer;
  176. }
  177. - (SLShotFocusView *)focusView {
  178. if (_focusView == nil) {
  179. _focusView= [[SLShotFocusView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
  180. }
  181. return _focusView;
  182. }
  183. - (UILabel *)tipsLabel {
  184. if (_tipsLabel == nil) {
  185. _tipsLabel = [[UILabel alloc] initWithFrame:CGRectMake((self.view.sl_width - 140)/2.0, self.shotBtn.sl_y - 20 - 30, 140, 20)];
  186. _tipsLabel.textColor = [UIColor whiteColor];
  187. _tipsLabel.font = [UIFont systemFontOfSize:14];
  188. _tipsLabel.textAlignment = NSTextAlignmentCenter;
  189. // _tipsLabel.text = @"轻触拍照,按住摄像";
  190. _tipsLabel.text = @"轻触拍照";
  191. }
  192. return _tipsLabel;
  193. }
  194. #pragma mark - HelpMethods
  195. //开始计时录制
  196. - (void)startTimer{
  197. /** 创建定时器对象
  198. * para1: DISPATCH_SOURCE_TYPE_TIMER 为定时器类型
  199. * para2-3: 中间两个参数对定时器无用
  200. * para4: 最后为在什么调度队列中使用
  201. */
  202. _gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
  203. /** 设置定时器
  204. * para2: 任务开始时间
  205. * para3: 任务的间隔
  206. * para4: 可接受的误差时间,设置0即不允许出现误差
  207. * Tips: 单位均为纳秒
  208. */
  209. //定时器延迟时间
  210. NSTimeInterval delayTime = 0.f;
  211. //定时器间隔时间
  212. NSTimeInterval timeInterval = 0.1f;
  213. //设置开始时间
  214. dispatch_time_t startDelayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC));
  215. dispatch_source_set_timer(_gcdTimer, startDelayTime, timeInterval * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
  216. /** 设置定时器任务
  217. * 可以通过block方式
  218. * 也可以通过C函数方式
  219. */
  220. // __weak typeof(self) weakSelf = self;
  221. dispatch_source_set_event_handler(_gcdTimer, ^{
  222. self->_durationOfVideo+= timeInterval;
  223. SL_DISPATCH_ON_MAIN_THREAD(^{
  224. //主线程更新UI
  225. self.progressLayer.strokeEnd = self->_durationOfVideo/KMaxDurationOfVideo;
  226. });
  227. if(self->_durationOfVideo > KMaxDurationOfVideo) {
  228. NSLog(@"时长 %f", self->_durationOfVideo);
  229. SL_DISPATCH_ON_MAIN_THREAD(^{
  230. self.progressLayer.strokeEnd = 1;
  231. //暂停定时器
  232. // dispatch_suspend(_gcdTimer);
  233. //取消计时器
  234. dispatch_source_cancel(self->_gcdTimer);
  235. self->_durationOfVideo = 0;
  236. [self.progressLayer removeFromSuperlayer];
  237. //停止录制
  238. [self.avCaptureTool stopRecordVideo];
  239. [self.avCaptureTool stopRunning];
  240. });
  241. }
  242. });
  243. // 启动任务,GCD计时器创建后需要手动启动
  244. dispatch_resume(_gcdTimer);
  245. }
  246. #pragma mark - EventsHandle
  247. //返回
  248. - (void)backBtn:(UIButton *)btn {
  249. [self dismissViewControllerAnimated:YES completion:^{
  250. if (self.takePhotoBlock) {
  251. self.takePhotoBlock(nil);
  252. }
  253. }];
  254. }
  255. //聚焦手势
  256. - (void)tapFocusing:(UITapGestureRecognizer *)tap {
  257. //如果没在运行,取消聚焦
  258. if(!self.avCaptureTool.isRunning) {
  259. return;
  260. }
  261. CGPoint point = [tap locationInView:self.captureView];
  262. if(point.y > self.shotBtn.sl_y || point.y < self.switchCameraBtn.sl_y + self.switchCameraBtn.sl_height) {
  263. return;
  264. }
  265. [self focusAtPoint:point];
  266. }
  267. //设置焦点视图位置
  268. - (void)focusAtPoint:(CGPoint)point {
  269. self.focusView.center = point;
  270. [self.focusView removeFromSuperview];
  271. [self.view addSubview:self.focusView];
  272. self.focusView.transform = CGAffineTransformMakeScale(1.3, 1.3);
  273. [UIView animateWithDuration:0.5 animations:^{
  274. self.focusView.transform = CGAffineTransformIdentity;
  275. }];
  276. [self.avCaptureTool focusAtPoint:point];
  277. SL_WeakSelf;
  278. [SLDelayPerform sl_startDelayPerform:^{
  279. [weakSelf.focusView removeFromSuperview];
  280. } afterDelay:1.0];
  281. }
  282. //调节焦距 手势
  283. - (void)pinchFocalLength:(UIPinchGestureRecognizer *)pinch {
  284. if(pinch.state == UIGestureRecognizerStateBegan) {
  285. self.currentZoomFactor = self.avCaptureTool.videoZoomFactor;
  286. }
  287. if (pinch.state == UIGestureRecognizerStateChanged) {
  288. self.avCaptureTool.videoZoomFactor = self.currentZoomFactor * pinch.scale;
  289. }
  290. }
  291. //切换前/后摄像头
  292. - (void)switchCameraClicked:(id)sender {
  293. if (self.avCaptureTool.devicePosition == AVCaptureDevicePositionFront) {
  294. [self.avCaptureTool switchsCamera:AVCaptureDevicePositionBack];
  295. } else if(self.avCaptureTool.devicePosition == AVCaptureDevicePositionBack) {
  296. [self.avCaptureTool switchsCamera:AVCaptureDevicePositionFront];
  297. }
  298. }
  299. //轻触拍照
  300. - (void)takePicture:(UITapGestureRecognizer *)tap {
  301. [self.avCaptureTool outputPhoto];
  302. NSLog(@"拍照");
  303. }
  304. //长按摄像 小视频
  305. - (void)recordVideo:(UILongPressGestureRecognizer *)longPress {
  306. switch (longPress.state) {
  307. case UIGestureRecognizerStateBegan:{
  308. self.shotBtn.sl_size = CGSizeMake(100, 100);
  309. self.shotBtn.center = CGPointMake(self.view.sl_width/2.0, self.view.sl_height - 80);
  310. self.shotBtn.layer.cornerRadius = self.shotBtn.sl_height/2.0;
  311. self.whiteView.sl_size = CGSizeMake(40, 40);
  312. self.whiteView.center = CGPointMake(self.shotBtn.sl_width/2.0, self.shotBtn.sl_height/2.0);
  313. self.whiteView.layer.cornerRadius = self.whiteView.sl_width/2.0;
  314. //开始计时
  315. [self startTimer];
  316. //添加进度条
  317. [self.shotBtn.layer addSublayer:self.progressLayer];
  318. self.progressLayer.strokeEnd = 0;
  319. NSString *outputVideoFielPath = [NSTemporaryDirectory() stringByAppendingString:@"myVideo.mp4"];
  320. //开始录制视频
  321. [self.avCaptureTool startRecordVideoToOutputFileAtPath:outputVideoFielPath recordType:SLAvCaptureTypeAv];
  322. }
  323. NSLog(@"开始摄像");
  324. break;
  325. case UIGestureRecognizerStateChanged:{
  326. }
  327. // NSLog(@"正在摄像");
  328. break;
  329. case UIGestureRecognizerStateEnded:{
  330. self.shotBtn.sl_size = CGSizeMake(70, 70);
  331. self.shotBtn.center = CGPointMake(self.view.sl_width/2.0, self.view.sl_height - 80);
  332. self.shotBtn.layer.cornerRadius = self.shotBtn.sl_height/2.0;
  333. self.whiteView.sl_size = CGSizeMake(50, 50);
  334. self.whiteView.center = CGPointMake(self.shotBtn.sl_width/2.0, self.shotBtn.sl_height/2.0);
  335. self.whiteView.layer.cornerRadius = self.whiteView.sl_width/2.0;
  336. //取消计时器
  337. dispatch_source_cancel(self->_gcdTimer);
  338. self->_durationOfVideo = 0;
  339. self.progressLayer.strokeEnd = 0;
  340. [self.progressLayer removeFromSuperlayer];
  341. // 结束录制视频
  342. [self.avCaptureTool stopRunning];
  343. [self.avCaptureTool stopRecordVideo];
  344. }
  345. break;
  346. default:
  347. break;
  348. }
  349. }
  350. // KVO
  351. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  352. if ([keyPath isEqualToString:@"shootingOrientation"]) {
  353. UIDeviceOrientation deviceOrientation = [change[@"new"] intValue];
  354. [UIView animateWithDuration:0.3 animations:^{
  355. switch (deviceOrientation) {
  356. case UIDeviceOrientationPortrait:
  357. self.switchCameraBtn.transform = CGAffineTransformMakeRotation(0);
  358. break;
  359. case UIDeviceOrientationLandscapeLeft:
  360. self.switchCameraBtn.transform = CGAffineTransformMakeRotation(M_PI/2.0);
  361. break;
  362. case UIDeviceOrientationLandscapeRight:
  363. self.switchCameraBtn.transform = CGAffineTransformMakeRotation(-M_PI/2.0);
  364. break;
  365. case UIDeviceOrientationPortraitUpsideDown:
  366. self.switchCameraBtn.transform = CGAffineTransformMakeRotation(-M_PI);
  367. break;
  368. default:
  369. break;
  370. }
  371. }];
  372. }
  373. }
  374. #pragma mark - SLAvCaptureToolDelegate 图片、音视频输出代理
  375. //图片输出完成
  376. - (void)captureTool:(SLAvCaptureTool *)captureTool didOutputPhoto:(UIImage *)image error:(NSError *)error {
  377. [self.avCaptureTool stopRunning];
  378. NSLog(@"拍照结束");
  379. SLEditImageController * editViewController = [[SLEditImageController alloc] init];
  380. editViewController.image = image;
  381. editViewController.isCheckBody = _isCheckBody;
  382. editViewController.modalPresentationStyle = UIModalPresentationFullScreen;
  383. [editViewController initEditPhotoBlock:^(UIImage * _Nonnull editImage) {
  384. if (self.takePhotoBlock && editImage) {
  385. [self dismissViewControllerAnimated:YES completion:^{
  386. self.takePhotoBlock(editImage);
  387. }];
  388. }
  389. }];
  390. [self presentViewController:editViewController animated:NO completion:nil];
  391. }
  392. - (void)initTakePhotoBlock:(TakePhotoBlock)block {
  393. self.takePhotoBlock = block;
  394. }
  395. //音视频输出完成
  396. - (void)captureTool:(SLAvCaptureTool *)captureTool didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL error:(NSError *)error {
  397. [self.avCaptureTool stopRunning];
  398. NSLog(@"结束录制");
  399. SLEditVideoController * editViewController = [[SLEditVideoController alloc] init];
  400. editViewController.videoPath = outputFileURL;
  401. editViewController.modalPresentationStyle = UIModalPresentationFullScreen;
  402. [self presentViewController:editViewController animated:NO completion:^{
  403. NSString *result = error ? @"录制失败" : @"录制成功";
  404. NSLog(@"%@ %@", result , error.localizedDescription);
  405. [SLAlertView showAlertViewWithText:result delayHid:1];
  406. }];
  407. // NSInteger fileSize = (NSInteger)[[NSFileManager defaultManager] attributesOfItemAtPath:outputFileURL.path error:nil].fileSize;
  408. // NSLog(@"视频文件大小 === %.2fM",fileSize/(1024.0*1024.0));
  409. }
  410. @end