SLEditVideoController.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. //
  2. // SLEditViewController.m
  3. // DarkMode
  4. //
  5. // Created by wsl on 2019/10/12.
  6. // Copyright © 2019 wsl. All rights reserved.
  7. //
  8. #import "SLEditVideoController.h"
  9. #import <Photos/Photos.h>
  10. #import "UIView+SLImage.h"
  11. #import "SLBlurView.h"
  12. #import "SLEditMenuView.h"
  13. #import "SLAvPlayer.h"
  14. #import "SLAvCaptureTool.h"
  15. #import "SLAvEditExport.h"
  16. #import "SLEditSelectedBox.h"
  17. #import "SLImage.h"
  18. #import "SLImageView.h"
  19. #import "SLDrawView.h"
  20. #import "SLEditTextView.h"
  21. #import "SLEditVideoClipping.h"
  22. #import "UIImage+SLCommon.h"
  23. @interface SLEditVideoController () <UIGestureRecognizerDelegate, SLAvPlayerDelegate>
  24. @property (nonatomic, strong) UIImageView *preview; // 预览视图 展示编辑的图片或视频
  25. @property (nonatomic, strong) SLAvPlayer *avPlayer; //视频播放预览
  26. @property (nonatomic, strong) SLBlurView *editBtn; //编辑
  27. @property (nonatomic, strong) SLBlurView *againShotBtn; // 再拍一次
  28. @property (nonatomic, strong) UIButton *saveAlbumBtn; //保存到相册
  29. @property (nonatomic, strong) UIButton *cancleEditBtn; //取消编辑
  30. @property (nonatomic, strong) UIButton *doneEditBtn; //完成编辑
  31. @property (nonatomic, strong) SLEditMenuView *editMenuView; //编辑菜单栏
  32. @property (nonatomic, strong) UIButton *trashTips; //垃圾桶提示 拖拽删除 贴图或文字
  33. @property (nonatomic, strong) SLDrawView *drawView; // 涂鸦视图
  34. @property (nonatomic, strong) NSMutableArray *watermarkArray; // 水印层 所有的贴图和文本
  35. @property (nonatomic, strong) SLEditSelectedBox *selectedBox; //水印选中框
  36. @property (nonatomic, strong) SLEditVideoClipping * videoClippingView; //视频裁剪 子菜单视图 选择裁剪范围
  37. @property (nonatomic, assign) CMTime clippingBeginTime; //视频裁剪起始点
  38. @property (nonatomic, assign) CMTime clippingEndTime; //视频裁剪结束点
  39. @end
  40. @implementation SLEditVideoController
  41. #pragma mark - Override
  42. - (void)viewDidLoad {
  43. [super viewDidLoad];
  44. [self setupUI];
  45. }
  46. - (void)viewWillDisappear:(BOOL)animated {
  47. [super viewWillDisappear:animated];
  48. _avPlayer.delegate = nil;
  49. [_avPlayer stop];
  50. _avPlayer = nil;
  51. }
  52. - (BOOL)prefersStatusBarHidden {
  53. return YES;
  54. }
  55. - (void)dealloc {
  56. NSLog(@"视频编辑视图释放了");
  57. }
  58. #pragma mark - UI
  59. - (void)setupUI {
  60. self.view.backgroundColor = [UIColor blackColor];
  61. [self.view addSubview:self.preview];
  62. self.avPlayer.url = self.videoPath;
  63. self.avPlayer.delegate = self;
  64. if (self.avPlayer.naturalSize.width != CGSizeZero.width) {
  65. self.preview.sl_height = self.preview.sl_width * self.avPlayer.naturalSize.height/ self.avPlayer.naturalSize.width;
  66. }
  67. self.avPlayer.monitor = self.preview;
  68. self.preview.center = CGPointMake(self.view.sl_width/2.0, self.view.sl_height/2.0);
  69. [self.avPlayer play];
  70. [self.view addSubview:self.againShotBtn];
  71. [self.view addSubview:self.editBtn];
  72. [self.view addSubview:self.saveAlbumBtn];
  73. [self.view addSubview:self.cancleEditBtn];
  74. [self.view addSubview:self.doneEditBtn];
  75. }
  76. #pragma mark - HelpMethods
  77. // 添加拖拽、缩放、旋转、单击、双击手势
  78. - (void)addRotateAndPinchGestureRecognizer:(UIView *)view {
  79. //单击手势选中
  80. UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapAction:)];
  81. singleTap.numberOfTapsRequired = 1;
  82. singleTap.numberOfTouchesRequired = 1;
  83. [view addGestureRecognizer:singleTap];
  84. if ([view isKindOfClass:[UILabel class]]) {
  85. UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapAction:)];
  86. doubleTap.numberOfTapsRequired = 2;
  87. doubleTap.numberOfTouchesRequired = 1;
  88. [singleTap requireGestureRecognizerToFail:doubleTap];
  89. [view addGestureRecognizer:doubleTap];
  90. }
  91. //拖拽手势
  92. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragAction:)];
  93. pan.minimumNumberOfTouches = 1;
  94. [view addGestureRecognizer:pan];
  95. //缩放手势
  96. UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
  97. action:@selector(pinchAction:)];
  98. pinchGestureRecognizer.delegate = self;
  99. [view addGestureRecognizer:pinchGestureRecognizer];
  100. //旋转手势
  101. UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self
  102. action:@selector(rotateAction:)];
  103. [view addGestureRecognizer:rotateRecognizer];
  104. rotateRecognizer.delegate = self;
  105. }
  106. //置顶视图
  107. - (void)topSelectedView:(UIView *)topView {
  108. [self.preview bringSubviewToFront:topView];
  109. [self.watermarkArray removeObject:topView];
  110. [self.watermarkArray addObject:topView];
  111. [SLDelayPerform sl_cancelDelayPerform]; //取消延迟执行
  112. self.selectedBox.frame = topView.bounds;
  113. [topView addSubview:self.selectedBox];
  114. }
  115. // 隐藏预览按钮
  116. - (void)hiddenPreviewButton:(BOOL)isHidden {
  117. self.againShotBtn.hidden = isHidden;
  118. self.editBtn.hidden = isHidden;
  119. self.saveAlbumBtn.hidden = isHidden;
  120. }
  121. // 隐藏编辑时菜单按钮
  122. - (void)hiddenEditMenus:(BOOL)isHidden {
  123. self.cancleEditBtn.hidden = isHidden;
  124. self.doneEditBtn.hidden = isHidden;
  125. self.editMenuView.hidden = isHidden;
  126. }
  127. // 视频的涂鸦层
  128. - (CALayer *)graffitiLayer {
  129. CALayer *graffitiLayer = [CALayer layer];
  130. graffitiLayer.frame = self.drawView.bounds;
  131. // 把水印在预览层上的坐标转换为视频资源文件上的坐标
  132. // 视频Layer上的坐标系原点在左下角,单位是px像素
  133. CGSize scaleSize = CGSizeMake(self.avPlayer.naturalSize.width/self.preview.sl_width, self.avPlayer.naturalSize.height/self.preview.sl_height);
  134. CGRect changeRect = CGRectMake(0, 0, CGRectGetWidth(graffitiLayer.frame)*scaleSize.width, CGRectGetHeight(graffitiLayer.frame)*scaleSize.height);
  135. graffitiLayer.frame = changeRect;
  136. UIImage *image = [self.drawView sl_imageByViewInRect:self.drawView.bounds];
  137. /** 缩放至视频大小 */
  138. UIGraphicsBeginImageContextWithOptions(self.avPlayer.naturalSize, NO, 1);
  139. [image drawInRect:CGRectMake(0, 0, self.avPlayer.naturalSize.width, self.avPlayer.naturalSize.height)];
  140. UIImage *graffitiImage = UIGraphicsGetImageFromCurrentImageContext();
  141. UIGraphicsEndImageContext();
  142. graffitiLayer.contentsScale = [UIScreen mainScreen].scale;
  143. graffitiLayer.contents = (__bridge id _Nullable)(graffitiImage.CGImage);
  144. return graffitiLayer;
  145. }
  146. // 视频的 贴画层 和 文本层
  147. - (NSMutableArray *)watermarkLayers {
  148. NSMutableArray *stickerLayers = [NSMutableArray array];
  149. for (UIView *view in self.watermarkArray) {
  150. CALayer *animatedLayer = [CALayer layer];
  151. animatedLayer.frame = view.bounds;
  152. // 把水印在预览层上的坐标转换为视频资源文件上的坐标
  153. // 视频Layer上的坐标系原点在左下角,单位是px像素
  154. CGSize scaleSize = CGSizeMake(self.avPlayer.naturalSize.width/self.preview.sl_width, self.avPlayer.naturalSize.height/self.preview.sl_height);
  155. CGRect changeRect = CGRectMake(0, 0, CGRectGetWidth(animatedLayer.frame)*scaleSize.width, CGRectGetHeight(animatedLayer.frame)*scaleSize.height);
  156. animatedLayer.frame = changeRect;
  157. animatedLayer.position = CGPointMake(view.center.x*scaleSize.width, (self.preview.sl_height - view.center.y)*scaleSize.height);
  158. //形变
  159. CGAffineTransform transform = view.transform;
  160. // 缩放系数
  161. CGFloat scale = sqrt(transform.a*transform.a + transform.c*transform.c);
  162. //反转 主要用来解决旋转反向的问题
  163. CGAffineTransform rotationTransform = CGAffineTransformInvert(transform);
  164. CGAffineTransform scaleTransform = CGAffineTransformScale(rotationTransform, scale, scale);
  165. animatedLayer.affineTransform = CGAffineTransformScale(scaleTransform, scale, scale);
  166. if ([view isKindOfClass:[SLImageView class]]) {
  167. SLImageView *imageView = (SLImageView *)view;
  168. if (imageView.imageType == SLImageTypeGIF) {
  169. CAKeyframeAnimation *gifLayerAnimation = [self animationForGifWithImage:imageView.animatedImage];
  170. gifLayerAnimation.beginTime = AVCoreAnimationBeginTimeAtZero;
  171. gifLayerAnimation.removedOnCompletion = NO;
  172. [animatedLayer addAnimation:gifLayerAnimation forKey:@"gif"];
  173. }else {
  174. animatedLayer.contentsScale = [UIScreen mainScreen].scale;
  175. animatedLayer.contents = (__bridge id _Nullable)(imageView.image.CGImage);
  176. }
  177. } else if ([view isKindOfClass:[UILabel class]]) {
  178. UILabel *label = (UILabel *)view;
  179. SLImage *image = [SLImage imageWithData:UIImagePNGRepresentation([label sl_imageByViewInRect:label.bounds])];
  180. animatedLayer.contentsScale = [UIScreen mainScreen].scale;
  181. animatedLayer.contents = (__bridge id _Nullable)(image.CGImage);
  182. }
  183. [stickerLayers addObject:animatedLayer];
  184. }
  185. return stickerLayers;
  186. }
  187. // Gif CALayer关键帧动画
  188. - (CAKeyframeAnimation *)animationForGifWithImage:(SLImage *)image {
  189. CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
  190. NSMutableArray * frames = [NSMutableArray new];
  191. NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
  192. CGFloat currentTime = 0;
  193. CGFloat totalTime = image.totalTime;
  194. NSInteger frameCount = image.frameCount;
  195. for (int i = 0; i < frameCount; ++i) {
  196. [times addObject:[NSNumber numberWithFloat:(currentTime / totalTime)]];
  197. currentTime += [image imageDurationAtIndex:i];
  198. [frames addObject:(__bridge id)[image imageAtIndex:i].CGImage];
  199. }
  200. animation.keyTimes = times;
  201. animation.values = frames;
  202. animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  203. animation.duration = totalTime;
  204. animation.repeatCount = HUGE_VALF;
  205. return animation;
  206. }
  207. #pragma mark - Getter
  208. - (UIImageView *)preview {
  209. if (_preview == nil) {
  210. _preview = [[UIImageView alloc] initWithFrame:self.view.bounds];
  211. _preview.contentMode = UIViewContentModeScaleAspectFit;
  212. _preview.backgroundColor = [UIColor blackColor];
  213. _preview.userInteractionEnabled = YES;
  214. _preview.clipsToBounds = YES;
  215. }
  216. return _preview;
  217. }
  218. - (SLAvPlayer *)avPlayer {
  219. if (!_avPlayer) {
  220. _avPlayer = [[SLAvPlayer alloc] init];
  221. }
  222. return _avPlayer;
  223. }
  224. - (SLBlurView *)editBtn {
  225. if (_editBtn == nil) {
  226. _editBtn = [[SLBlurView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
  227. _editBtn.center = CGPointMake(self.view.sl_width/2.0, self.view.sl_height - 80);
  228. _editBtn.layer.cornerRadius = _editBtn.sl_width/2.0;
  229. UIButton * btn = [[UIButton alloc] initWithFrame:_editBtn.bounds];
  230. [btn setImage:[UIImage imageNamed:@"edit"] forState:UIControlStateNormal];
  231. [btn addTarget:self action:@selector(editBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  232. [_editBtn addSubview:btn];
  233. }
  234. return _editBtn;
  235. }
  236. - (SLBlurView *)againShotBtn {
  237. if (_againShotBtn == nil) {
  238. _againShotBtn = [[SLBlurView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
  239. _againShotBtn.center = CGPointMake((self.view.sl_width/2 - 70/2.0)/2.0, self.view.sl_height - 80);
  240. _againShotBtn.layer.cornerRadius = _againShotBtn.sl_width/2.0;
  241. UIButton * btn = [[UIButton alloc] initWithFrame:_againShotBtn.bounds];
  242. [btn setImage:[UIImage imageNamed:@"cancle"] forState:UIControlStateNormal];
  243. [btn addTarget:self action:@selector(againShotBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  244. [_againShotBtn addSubview:btn];
  245. }
  246. return _againShotBtn;
  247. }
  248. - (UIButton *)saveAlbumBtn {
  249. if (_saveAlbumBtn == nil) {
  250. _saveAlbumBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
  251. _saveAlbumBtn.center = CGPointMake(self.view.sl_width/2.0 + 70/2.0+ (self.view.sl_width/2 - 70/2.0)/2.0, self.view.sl_height - 80);
  252. _saveAlbumBtn.layer.cornerRadius = _saveAlbumBtn.sl_width/2.0;
  253. _saveAlbumBtn.backgroundColor = [UIColor whiteColor];
  254. [_saveAlbumBtn setImage:[UIImage imageNamed:@"save"] forState:UIControlStateNormal];
  255. [_saveAlbumBtn addTarget:self action:@selector(saveAlbumBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  256. }
  257. return _saveAlbumBtn;
  258. }
  259. - (UIButton *)cancleEditBtn {
  260. if (_cancleEditBtn == nil) {
  261. _cancleEditBtn = [[UIButton alloc] initWithFrame:CGRectMake(15, 30, 40, 30)];
  262. _cancleEditBtn.hidden = YES;
  263. [_cancleEditBtn setTitle:@"取消" forState:UIControlStateNormal];
  264. [_cancleEditBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  265. _cancleEditBtn.titleLabel.font = [UIFont systemFontOfSize:14];
  266. [_cancleEditBtn addTarget:self action:@selector(cancleEditBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  267. }
  268. return _cancleEditBtn;
  269. }
  270. - (UIButton *)doneEditBtn {
  271. if (_doneEditBtn == nil) {
  272. _doneEditBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.sl_width - 50 - 15, 30, 40, 30)];
  273. _doneEditBtn.hidden = YES;
  274. _doneEditBtn.backgroundColor = [UIColor colorWithRed:45/255.0 green:175/255.0 blue:45/255.0 alpha:1];
  275. [_doneEditBtn setTitle:@"完成" forState:UIControlStateNormal];
  276. [_doneEditBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  277. _doneEditBtn.titleLabel.font = [UIFont systemFontOfSize:14];
  278. _doneEditBtn.layer.cornerRadius = 4;
  279. [_doneEditBtn addTarget:self action:@selector(doneEditBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  280. }
  281. return _doneEditBtn;
  282. }
  283. - (SLEditMenuView *)editMenuView {
  284. if (!_editMenuView) {
  285. _editMenuView = [[SLEditMenuView alloc] initWithFrame:CGRectMake(0, self.view.sl_height - 80 - 60, self.view.sl_width, 80 + 60)];
  286. _editMenuView.hidden = YES;
  287. _editMenuView.editObject = SLEditObjectVideo;
  288. __weak typeof(self) weakSelf = self;
  289. _editMenuView.selectEditMenu = ^(SLEditMenuType editMenuType, NSDictionary * _Nullable setting) {
  290. if (editMenuType == SLEditMenuTypeGraffiti) {
  291. weakSelf.drawView.userInteractionEnabled = ![setting[@"hidden"] boolValue];
  292. [weakSelf.preview insertSubview:weakSelf.drawView atIndex:1];
  293. if (setting[@"lineColor"]) {
  294. weakSelf.drawView.lineColor = setting[@"lineColor"];
  295. }
  296. if (setting[@"goBack"]) {
  297. [weakSelf.drawView goBack];
  298. }
  299. }else {
  300. weakSelf.drawView.userInteractionEnabled = NO;
  301. }
  302. if (editMenuType == SLEditMenuTypeSticking) {
  303. SLImage *image = setting[@"image"];
  304. if (image) {
  305. SLImageView *imageView = [[SLImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width/[UIScreen mainScreen].scale, image.size.height/[UIScreen mainScreen].scale)];
  306. imageView.autoPlayAnimatedImage = YES;
  307. imageView.userInteractionEnabled = YES;
  308. imageView.center = CGPointMake(weakSelf.preview.sl_width/2.0, weakSelf.preview.sl_height/2.0);
  309. imageView.image = image;
  310. [weakSelf.watermarkArray addObject:imageView];
  311. [weakSelf.preview addSubview:imageView];
  312. [weakSelf addRotateAndPinchGestureRecognizer:imageView];
  313. [weakSelf topSelectedView:imageView];
  314. [SLDelayPerform sl_startDelayPerform:^{
  315. [weakSelf.selectedBox removeFromSuperview];
  316. } afterDelay:1.0];
  317. }
  318. }
  319. if (editMenuType == SLEditMenuTypeText) {
  320. SLEditTextView *editTextView = [[SLEditTextView alloc] initWithFrame:CGRectMake(0, 0, SL_kScreenWidth, SL_kScreenHeight)];
  321. [weakSelf.view addSubview:editTextView];
  322. editTextView.editTextCompleted = ^(UILabel * _Nullable label) {
  323. if (label.text.length == 0 || label == nil) {
  324. return;
  325. }
  326. label.center = CGPointMake(weakSelf.preview.sl_width/2.0, weakSelf.preview.sl_height/2.0);
  327. [weakSelf.preview addSubview:label];
  328. [weakSelf.watermarkArray addObject:label];
  329. [weakSelf addRotateAndPinchGestureRecognizer:label];
  330. [weakSelf topSelectedView:label];
  331. [SLDelayPerform sl_startDelayPerform:^{
  332. [weakSelf.selectedBox removeFromSuperview];
  333. } afterDelay:1.0];
  334. };
  335. }
  336. if(editMenuType == SLEditMenuTypeVideoClipping) {
  337. weakSelf.videoClippingView.exitClipping = ^{
  338. [weakSelf hiddenEditMenus:NO];
  339. weakSelf.preview.transform = CGAffineTransformIdentity;
  340. weakSelf.preview.center = CGPointMake(SL_kScreenWidth/2.0, SL_kScreenHeight/2.0);
  341. };
  342. [weakSelf hiddenEditMenus:YES];
  343. weakSelf.preview.transform = CGAffineTransformMakeScale((SL_kScreenWidth - 30 * 2)/SL_kScreenWidth, (SL_kScreenWidth - 30 * 2)/SL_kScreenWidth);
  344. weakSelf.preview.center = CGPointMake(SL_kScreenWidth/2.0, (SL_kScreenHeight - weakSelf.videoClippingView.sl_height)/2.0);
  345. weakSelf.videoClippingView.asset = [AVAsset assetWithURL:weakSelf.videoPath];
  346. [weakSelf.view addSubview:weakSelf.videoClippingView];
  347. }
  348. };
  349. [self.view addSubview:_editMenuView];
  350. }
  351. return _editMenuView;
  352. }
  353. - (UIButton *)trashTips {
  354. if (!_trashTips) {
  355. _trashTips = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
  356. _trashTips.center = CGPointMake(SL_kScreenWidth/2.0, SL_kScreenHeight - 60);
  357. [_trashTips setTitle:@"拖动到此处删除" forState:UIControlStateNormal];
  358. [_trashTips setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  359. _trashTips.titleLabel.font = [UIFont systemFontOfSize:14];
  360. }
  361. return _trashTips;
  362. }
  363. - (SLDrawView *)drawView {
  364. if (!_drawView) {
  365. _drawView = [[SLDrawView alloc] initWithFrame:self.preview.bounds];
  366. _drawView.backgroundColor = [UIColor clearColor];
  367. __weak typeof(self) weakSelf = self;
  368. _drawView.drawBegan = ^{
  369. [weakSelf hiddenEditMenus:YES];
  370. };
  371. _drawView.drawEnded = ^{
  372. [weakSelf hiddenEditMenus:NO];
  373. };
  374. }
  375. return _drawView;
  376. }
  377. - (NSMutableArray *)watermarkArray {
  378. if (!_watermarkArray) {
  379. _watermarkArray = [NSMutableArray array];
  380. }
  381. return _watermarkArray;
  382. }
  383. - (SLEditSelectedBox *)selectedBox {
  384. if (!_selectedBox) {
  385. _selectedBox = [[SLEditSelectedBox alloc] init];
  386. }
  387. return _selectedBox;
  388. }
  389. - (SLEditVideoClipping *)videoClippingView {
  390. if (!_videoClippingView) {
  391. _videoClippingView = [[SLEditVideoClipping alloc] initWithFrame:CGRectMake(0, SL_kScreenHeight - 110, SL_kScreenWidth, 110)];
  392. _videoClippingView.backgroundColor = [UIColor blackColor];
  393. __weak typeof(self) weakSelf = self;
  394. _videoClippingView.selectedClippingBegin = ^(CMTime beginTime, CMTime endTime, UIGestureRecognizerState state) {
  395. [weakSelf.avPlayer seekToTime:beginTime completionHandler:nil];
  396. if (state == UIGestureRecognizerStateEnded) {
  397. weakSelf.clippingBeginTime = beginTime;
  398. [weakSelf.avPlayer play];
  399. NSLog(@"裁剪范围:%.2f %.2f",CMTimeGetSeconds(weakSelf.clippingBeginTime), CMTimeGetSeconds(weakSelf.clippingEndTime));
  400. }
  401. };
  402. _videoClippingView.selectedClippingEnd = ^(CMTime beginTime, CMTime endTime, UIGestureRecognizerState state) {
  403. [weakSelf.avPlayer seekToTime:endTime completionHandler:nil];
  404. if (state == UIGestureRecognizerStateEnded) {
  405. weakSelf.clippingEndTime = endTime;
  406. [weakSelf.avPlayer seekToTime:beginTime completionHandler:nil];
  407. [weakSelf.avPlayer play];
  408. NSLog(@"裁剪范围:%.2f %.2f",CMTimeGetSeconds(weakSelf.clippingBeginTime), CMTimeGetSeconds(weakSelf.clippingEndTime));
  409. }
  410. };
  411. }
  412. return _videoClippingView;
  413. }
  414. - (CMTime)clippingBeginTime {
  415. if (_clippingBeginTime.value == 0) {
  416. _clippingBeginTime = CMTimeMake(0, self.clippingEndTime.timescale);
  417. }
  418. return _clippingBeginTime;
  419. }
  420. - (CMTime)clippingEndTime {
  421. if (_clippingEndTime.value == 0) {
  422. _clippingEndTime = self.avPlayer.duration;
  423. }
  424. return _clippingEndTime;
  425. }
  426. #pragma mark - Events Handle
  427. //编辑
  428. - (void)editBtnClicked:(id)sender {
  429. [self hiddenEditMenus:NO];
  430. [self hiddenPreviewButton:YES];
  431. }
  432. //再试一次 继续拍摄
  433. - (void)againShotBtnClicked:(id)sender {
  434. [self dismissViewControllerAnimated:NO completion:nil];
  435. }
  436. //保存到相册
  437. - (void)saveAlbumBtnClicked:(id)sender {
  438. //视频录入完成之后在将视频保存到相簿 如果视频过大的话,建议创建一个后台任务去保存到相册
  439. PHPhotoLibrary *photoLibrary = [PHPhotoLibrary sharedPhotoLibrary];
  440. [photoLibrary performChanges:^{
  441. [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:self.videoPath];
  442. } completionHandler:^(BOOL success, NSError * _Nullable error) {
  443. SL_DISPATCH_ON_MAIN_THREAD(^{
  444. [self againShotBtnClicked:nil];
  445. });
  446. NSString *result = success ? @"视频保存至相册 成功" : @"保存视频到相册 失败 ";
  447. NSLog(@"%@", result);
  448. SL_DISPATCH_ON_MAIN_THREAD(^{
  449. [SLAlertView showAlertViewWithText:result delayHid:1];
  450. });
  451. }];
  452. }
  453. //保存图片完成后调用的方法
  454. - (void)savedPhotoImage:(UIImage*)image didFinishSavingWithError:(NSError *)error contextInfo: (void *)contextInfo {
  455. SL_DISPATCH_ON_MAIN_THREAD(^{
  456. [self dismissViewControllerAnimated:NO completion:^{
  457. NSString *result = error ? @"图片保存至相册 失败" : @"图片保存到相册 成功";
  458. NSLog(@"%@", result);
  459. [SLAlertView showAlertViewWithText:result delayHid:1];
  460. }];
  461. });
  462. }
  463. //取消编辑
  464. - (void)cancleEditBtnClicked:(id)sender {
  465. [self hiddenPreviewButton:NO];
  466. [self hiddenEditMenus:YES];
  467. [self.selectedBox removeFromSuperview];
  468. [_editMenuView removeFromSuperview];
  469. _editMenuView = nil;
  470. [_drawView removeFromSuperview];
  471. _drawView = nil;
  472. for (UIView *view in self.watermarkArray) {
  473. [view removeFromSuperview];
  474. }
  475. [self.watermarkArray removeAllObjects];
  476. _videoClippingView = nil;
  477. self.clippingBeginTime = kCMTimeZero;
  478. self.clippingEndTime = kCMTimeZero;
  479. }
  480. //完成编辑 导出编辑后的对象
  481. - (void)doneEditBtnClicked:(id)sender {
  482. [self.selectedBox removeFromSuperview];
  483. [self exportEditVideo];
  484. }
  485. //导出编辑后的视频
  486. - (void)exportEditVideo {
  487. UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  488. activityIndicatorView.frame = CGRectMake(0, 0, 50, 50);
  489. activityIndicatorView.color = [UIColor colorWithRed:45/255.0 green:175/255.0 blue:45/255.0 alpha:1];
  490. activityIndicatorView.center = CGPointMake(SL_kScreenWidth/2.0, SL_kScreenHeight/2.0);
  491. [activityIndicatorView startAnimating];
  492. [self.view addSubview:activityIndicatorView];
  493. // NSString *myBundlePath = [[NSBundle mainBundle] pathForResource:@"Resources" ofType:@"bundle"];
  494. // NSBundle *myBundle = [NSBundle bundleWithPath:myBundlePath];
  495. // NSString *audioPath = [myBundle pathForResource:@"The love of one's life" ofType:@"mp3" inDirectory:@"Audio"];
  496. // NSURL *bgsoundUrl = [NSURL fileURLWithPath:audioPath];
  497. SLAvEditExport *videoExportSession = [[SLAvEditExport alloc] initWithAsset:[AVAsset assetWithURL:self.videoPath]];
  498. NSString *outputVideoFielPath = [NSTemporaryDirectory() stringByAppendingString:@"EditMyVideo.mp4"];
  499. videoExportSession.outputURL = [NSURL fileURLWithPath:outputVideoFielPath];
  500. videoExportSession.timeRange = CMTimeRangeMake(self.clippingBeginTime,CMTimeSubtract(self.clippingEndTime, self.clippingBeginTime));
  501. videoExportSession.graffitiLayer = [self graffitiLayer];
  502. videoExportSession.stickerLayers = [self watermarkLayers];
  503. // videoExportSession.audioUrls = @[bgsoundUrl];
  504. videoExportSession.isNativeAudio = YES;
  505. [videoExportSession exportAsynchronouslyWithCompletionHandler:^(NSError * _Nonnull error) {
  506. self.avPlayer .url = videoExportSession.outputURL;
  507. self.avPlayer .delegate = self;
  508. self.videoPath = videoExportSession.outputURL;
  509. [self cancleEditBtnClicked:nil];
  510. [activityIndicatorView stopAnimating];
  511. [activityIndicatorView removeFromSuperview];
  512. NSString *result = error ? @"导出失败" : @"导出成功";
  513. [SLAlertView showAlertViewWithText:result delayHid:1];
  514. } progress:^(float progress) {
  515. // NSLog(@"视频导出进度 %f",progress);
  516. }];
  517. }
  518. // 点击水印视图
  519. - (void)singleTapAction:(UITapGestureRecognizer *)singleTap {
  520. [self topSelectedView:singleTap.view];
  521. if (singleTap.state == UIGestureRecognizerStateFailed || singleTap.state == UIGestureRecognizerStateEnded) {
  522. [SLDelayPerform sl_startDelayPerform:^{
  523. [self.selectedBox removeFromSuperview];
  524. } afterDelay:1.0];
  525. }
  526. }
  527. //双击 文本水印 开始编辑文本
  528. - (void)doubleTapAction:(UITapGestureRecognizer *)doubleTap {
  529. [self topSelectedView:doubleTap.view];
  530. doubleTap.view.hidden = YES;
  531. UILabel *tapLabel = (UILabel *)doubleTap.view;
  532. SLEditTextView *editTextView = [[SLEditTextView alloc] initWithFrame:CGRectMake(0, 0, SL_kScreenWidth, SL_kScreenHeight)];
  533. editTextView.configureEditParameters(@{@"textColor":tapLabel.textColor, @"backgroundColor":tapLabel.backgroundColor, @"text":tapLabel.text});
  534. editTextView.editTextCompleted = ^(UILabel * _Nullable label) {
  535. doubleTap.view.hidden = NO;
  536. if (label == nil) {
  537. return;
  538. }
  539. label.transform = tapLabel.transform;
  540. label.center = tapLabel.center;
  541. [tapLabel removeFromSuperview];
  542. [self.watermarkArray removeObject:tapLabel];
  543. [self.watermarkArray addObject:label];
  544. [self.preview addSubview:label];
  545. [self addRotateAndPinchGestureRecognizer:label];
  546. [self topSelectedView:label];
  547. [SLDelayPerform sl_startDelayPerform:^{
  548. [self.selectedBox removeFromSuperview];
  549. } afterDelay:1.0];
  550. };
  551. [self.view addSubview:editTextView];
  552. }
  553. // 拖拽 水印视图
  554. - (void)dragAction:(UIPanGestureRecognizer *)pan {
  555. // 返回的是相对于最原始的手指的偏移量
  556. CGPoint transP = [pan translationInView:self.preview];
  557. if (pan.state == UIGestureRecognizerStateBegan) {
  558. self.preview.clipsToBounds = NO;
  559. [self hiddenEditMenus:YES];
  560. [self.view addSubview:self.trashTips];
  561. [self topSelectedView:pan.view];
  562. } else if (pan.state == UIGestureRecognizerStateChanged ) {
  563. pan.view.center = CGPointMake(pan.view.center.x + transP.x, pan.view.center.y + transP.y);
  564. [pan setTranslation:CGPointZero inView:self.preview];
  565. //获取拖拽的视图在屏幕上的位置
  566. CGRect rect = [pan.view convertRect: pan.view.bounds toView:self.view];
  567. //是否删除 删除视图Y < 视图中心点Y坐标
  568. if (self.trashTips.center.y < rect.origin.y+rect.size.height/2.0) {
  569. [self.trashTips setTitle:@"松手即可删除" forState:UIControlStateNormal];
  570. [self.trashTips setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
  571. }else {
  572. [self.trashTips setTitle:@"拖动到此处删除" forState:UIControlStateNormal];
  573. [self.trashTips setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  574. }
  575. } else if (pan.state == UIGestureRecognizerStateFailed || pan.state == UIGestureRecognizerStateEnded) {
  576. [self hiddenEditMenus:NO];
  577. self.preview.clipsToBounds = YES;
  578. //获取拖拽的视图在屏幕上的位置
  579. CGRect rect = [pan.view convertRect: pan.view.bounds toView:self.view];
  580. CGRect previewRect = [self.view convertRect:self.preview.frame toView:self.view];
  581. //删除拖拽的视图
  582. if (self.trashTips.center.y < rect.origin.y+rect.size.height/2.0) {
  583. [pan.view removeFromSuperview];
  584. [self.watermarkArray removeObject:(SLImageView *)pan.view];
  585. }else if (!CGRectIntersectsRect(previewRect, rect)) {
  586. //如果出了父视图preview的范围,则置于父视图中心
  587. pan.view.center = CGPointMake(self.preview.sl_width/2.0, self.preview.sl_height/2.0);
  588. }
  589. [self.trashTips removeFromSuperview];
  590. [SLDelayPerform sl_startDelayPerform:^{
  591. [self.selectedBox removeFromSuperview];
  592. } afterDelay:1.0];
  593. }
  594. }
  595. //缩放 水印视图
  596. - (void)pinchAction:(UIPinchGestureRecognizer *)pinch {
  597. if (pinch.state == UIGestureRecognizerStateBegan) {
  598. [self topSelectedView:pinch.view];
  599. }else if (pinch.state == UIGestureRecognizerStateFailed || pinch.state == UIGestureRecognizerStateEnded){
  600. [SLDelayPerform sl_startDelayPerform:^{
  601. [self.selectedBox removeFromSuperview];
  602. } afterDelay:1.0];
  603. }
  604. pinch.view.transform = CGAffineTransformScale(pinch.view.transform, pinch.scale, pinch.scale);
  605. pinch.scale = 1.0;
  606. }
  607. //旋转 水印视图 注意:旋转之后的frame会变!!!
  608. - (void)rotateAction:(UIRotationGestureRecognizer *)rotation {
  609. if (rotation.state == UIGestureRecognizerStateBegan) {
  610. [self topSelectedView:rotation.view];
  611. }else if (rotation.state == UIGestureRecognizerStateFailed || rotation.state == UIGestureRecognizerStateEnded){
  612. [SLDelayPerform sl_startDelayPerform:^{
  613. [self.selectedBox removeFromSuperview];
  614. } afterDelay:1.0];
  615. }
  616. rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation);
  617. // 将旋转的弧度清零(注意不是将图片旋转的弧度清零, 而是将当前手指旋转的弧度清零)
  618. rotation.rotation = 0;
  619. }
  620. #pragma mark - UIGestureRecognizerDelegate
  621. // 该方法返回的BOOL值决定了view是否能够同时响应多个手势
  622. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
  623. // NSLog(@"%@ - %@", gestureRecognizer.class, otherGestureRecognizer.class);
  624. return YES;
  625. }
  626. #pragma mark - SLAvPlayerDelegate
  627. - (void)avPlayer:(SLAvPlayer *)avPlayer playingToCurrentTime:(CMTime)currentTime totalTime:(CMTime)totalTime {
  628. if (CMTimeGetSeconds(currentTime) >= CMTimeGetSeconds(self.clippingEndTime)) {
  629. [avPlayer seekToTime:self.clippingBeginTime completionHandler:nil];
  630. [avPlayer play];
  631. }
  632. }
  633. @end