猎豆优选

ZLEditVideoController.m 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. //
  2. // ZLEditVideoController.m
  3. // ZLPhotoBrowser
  4. //
  5. // Created by long on 2017/9/15.
  6. // Copyright © 2017年 long. All rights reserved.
  7. //
  8. #import "ZLEditVideoController.h"
  9. #import "ZLDefine.h"
  10. #import "ZLPhotoBrowser.h"
  11. #import <AVFoundation/AVFoundation.h>
  12. #import "ZLPhotoManager.h"
  13. #import "ZLPhotoModel.h"
  14. #import "ZLProgressHUD.h"
  15. #import "ToastUtils.h"
  16. #import <objc/runtime.h>
  17. #define kItemWidth kItemHeight * 2/3
  18. #define kItemHeight 50
  19. ///////-----cell
  20. @interface ZLEditVideoCell : UICollectionViewCell
  21. @property (nonatomic, strong) UIImageView *imageView;
  22. @end
  23. @implementation ZLEditVideoCell
  24. - (UIImageView *)imageView
  25. {
  26. if (!_imageView) {
  27. _imageView = [[UIImageView alloc] init];
  28. _imageView.frame = self.bounds;
  29. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  30. _imageView.clipsToBounds = YES;
  31. [self.contentView addSubview:_imageView];
  32. }
  33. return _imageView;
  34. }
  35. @end
  36. @protocol ZLEditFrameViewDelegate <NSObject>
  37. - (void)editViewValidRectChanged;
  38. - (void)editViewValidRectEndChanged;
  39. @end
  40. ///////-----编辑框
  41. @interface ZLEditFrameView : UIView
  42. {
  43. UIImageView *_leftView;
  44. UIImageView *_rightView;
  45. }
  46. @property (nonatomic, assign) CGRect validRect;
  47. @property (nonatomic, weak) id<ZLEditFrameViewDelegate> delegate;
  48. @end
  49. @implementation ZLEditFrameView
  50. - (instancetype)init
  51. {
  52. self = [super init];
  53. if (self) {
  54. [self setupUI];
  55. }
  56. return self;
  57. }
  58. - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
  59. {
  60. //扩大下有效范围
  61. CGRect left = _leftView.frame;
  62. left.origin.x -= kItemWidth/2;
  63. left.size.width += kItemWidth/2;
  64. CGRect right = _rightView.frame;
  65. right.size.width += kItemWidth/2;
  66. if (CGRectContainsPoint(left, point)) {
  67. return _leftView;
  68. }
  69. if (CGRectContainsPoint(right, point)) {
  70. return _rightView;
  71. }
  72. return nil;
  73. }
  74. - (void)setupUI
  75. {
  76. self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5];
  77. self.layer.borderWidth = 2;
  78. self.layer.borderColor = [UIColor clearColor].CGColor;
  79. _leftView = [[UIImageView alloc] initWithImage:GetImageWithName(@"zl_ic_left")];
  80. _leftView.userInteractionEnabled = YES;
  81. _leftView.tag = 0;
  82. UIPanGestureRecognizer *lg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
  83. [_leftView addGestureRecognizer:lg];
  84. [self addSubview:_leftView];
  85. _rightView = [[UIImageView alloc] initWithImage:GetImageWithName(@"zl_ic_right")];
  86. _rightView.userInteractionEnabled = YES;
  87. _rightView.tag = 1;
  88. UIPanGestureRecognizer *rg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
  89. [_rightView addGestureRecognizer:rg];
  90. [self addSubview:_rightView];
  91. }
  92. - (void)panAction:(UIGestureRecognizer *)pan
  93. {
  94. self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:.4].CGColor;
  95. CGPoint point = [pan locationInView:self];
  96. CGRect rct = self.validRect;
  97. const CGFloat W = GetViewWidth(self);
  98. CGFloat minX = 0;
  99. CGFloat maxX = W;
  100. switch (pan.view.tag) {
  101. case 0: {
  102. //left
  103. maxX = rct.origin.x + rct.size.width - kItemWidth;
  104. point.x = MAX(minX, MIN(point.x, maxX));
  105. point.y = 0;
  106. rct.size.width -= (point.x - rct.origin.x);
  107. rct.origin.x = point.x;
  108. }
  109. break;
  110. case 1:
  111. {
  112. //right
  113. minX = rct.origin.x + kItemWidth/2;
  114. maxX = W - kItemWidth/2;
  115. point.x = MAX(minX, MIN(point.x, maxX));
  116. point.y = 0;
  117. rct.size.width = (point.x - rct.origin.x + kItemWidth/2);
  118. }
  119. break;
  120. }
  121. switch (pan.state) {
  122. case UIGestureRecognizerStateBegan:
  123. case UIGestureRecognizerStateChanged:
  124. if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectChanged)]) {
  125. [self.delegate editViewValidRectChanged];
  126. }
  127. break;
  128. case UIGestureRecognizerStateEnded:
  129. case UIGestureRecognizerStateCancelled:
  130. self.layer.borderColor = [UIColor clearColor].CGColor;
  131. if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectEndChanged)]) {
  132. [self.delegate editViewValidRectEndChanged];
  133. }
  134. break;
  135. default:
  136. break;
  137. }
  138. self.validRect = rct;
  139. }
  140. - (void)setValidRect:(CGRect)validRect
  141. {
  142. _validRect = validRect;
  143. _leftView.frame = CGRectMake(validRect.origin.x, 0, kItemWidth/2, kItemHeight);
  144. _rightView.frame = CGRectMake(validRect.origin.x+validRect.size.width-kItemWidth/2, 0, kItemWidth/2, kItemHeight);
  145. [self setNeedsDisplay];
  146. }
  147. - (void)drawRect:(CGRect)rect
  148. {
  149. CGContextRef context = UIGraphicsGetCurrentContext();
  150. CGContextClearRect(context, self.validRect);
  151. CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
  152. CGContextSetLineWidth(context, 4.0);
  153. CGPoint topPoints[2];
  154. topPoints[0] = CGPointMake(self.validRect.origin.x, 0);
  155. topPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, 0);
  156. CGPoint bottomPoints[2];
  157. bottomPoints[0] = CGPointMake(self.validRect.origin.x, kItemHeight);
  158. bottomPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, kItemHeight);
  159. CGContextAddLines(context, topPoints, 2);
  160. CGContextAddLines(context, bottomPoints, 2);
  161. CGContextDrawPath(context, kCGPathStroke);
  162. }
  163. @end
  164. ///////-----editvc
  165. @interface ZLEditVideoController () <UIScrollViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, ZLEditFrameViewDelegate>
  166. {
  167. UIView *_bottomView;
  168. UIButton *_cancelBtn;
  169. UIButton *_doneBtn;
  170. NSTimer *_timer;
  171. //下方collectionview偏移量
  172. CGFloat _offsetX;
  173. BOOL _orientationChanged;
  174. UIView *_indicatorLine;
  175. AVAsset *_avAsset;
  176. NSTimeInterval _interval;
  177. NSInteger _measureCount;
  178. NSOperationQueue *_queue;
  179. NSMutableDictionary<NSString *, UIImage *> *_imageCache;
  180. NSMutableDictionary<NSString *, NSBlockOperation *> *_opCache;
  181. }
  182. @property (nonatomic, strong) AVPlayerLayer *playerLayer;
  183. @property (nonatomic, strong) UICollectionView *collectionView;
  184. @property (nonatomic, strong) ZLEditFrameView *editView;
  185. @property (nonatomic, strong) AVAssetImageGenerator *generator;
  186. @end
  187. @implementation ZLEditVideoController
  188. - (void)dealloc
  189. {
  190. [_queue cancelAllOperations];
  191. [self stopTimer];
  192. [[NSNotificationCenter defaultCenter] removeObserver:self];
  193. // NSLog(@"---- %s", __FUNCTION__);
  194. }
  195. - (AVAssetImageGenerator *)generator
  196. {
  197. if (!_generator) {
  198. _generator = [[AVAssetImageGenerator alloc] initWithAsset:_avAsset];
  199. _generator.maximumSize = CGSizeMake(kItemWidth*4, kItemHeight*4);
  200. _generator.appliesPreferredTrackTransform = YES;
  201. _generator.requestedTimeToleranceBefore = kCMTimeZero;
  202. _generator.requestedTimeToleranceAfter = kCMTimeZero;
  203. _generator.apertureMode = AVAssetImageGeneratorApertureModeProductionAperture;
  204. }
  205. return _generator;
  206. }
  207. - (void)viewDidLoad {
  208. [super viewDidLoad];
  209. [self setupUI];
  210. [self analysisAssetImages];
  211. _queue = [[NSOperationQueue alloc] init];
  212. _queue.maxConcurrentOperationCount = 3;
  213. _imageCache = [NSMutableDictionary dictionary];
  214. _opCache = [NSMutableDictionary dictionary];
  215. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
  216. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appResignActive) name:UIApplicationWillResignActiveNotification object:nil];
  217. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  218. }
  219. - (void)viewWillAppear:(BOOL)animated
  220. {
  221. [super viewWillAppear:animated];
  222. [UIApplication sharedApplication].statusBarHidden = YES;
  223. self.navigationController.navigationBar.hidden = YES;
  224. }
  225. - (void)viewWillDisappear:(BOOL)animated
  226. {
  227. [super viewWillDisappear:animated];
  228. self.navigationController.navigationBar.hidden = NO;
  229. }
  230. - (void)viewDidLayoutSubviews
  231. {
  232. [super viewDidLayoutSubviews];
  233. UIEdgeInsets inset = UIEdgeInsetsZero;
  234. if (@available(iOS 11, *)) {
  235. inset = self.view.safeAreaInsets;
  236. }
  237. self.playerLayer.frame = CGRectMake(15, inset.top>0?inset.top:30, kViewWidth-30, kViewHeight-160-inset.bottom);
  238. self.editView.frame = CGRectMake((kViewWidth-kItemWidth*10)/2, kViewHeight-100-inset.bottom, kItemWidth*10, kItemHeight);
  239. self.editView.validRect = self.editView.bounds;
  240. self.collectionView.frame = CGRectMake(inset.left, kViewHeight-100-inset.bottom, kViewWidth-inset.left-inset.right, kItemHeight);
  241. CGFloat leftOffset = ((kViewWidth-kItemWidth*10)/2-inset.left);
  242. CGFloat rightOffset = ((kViewWidth-kItemWidth*10)/2-inset.right);
  243. [self.collectionView setContentInset:UIEdgeInsetsMake(0, leftOffset, 0, rightOffset)];
  244. [self.collectionView setContentOffset:CGPointMake(_offsetX-leftOffset, 0)];
  245. CGFloat bottomViewH = 44;
  246. CGFloat bottomBtnH = 30;
  247. _bottomView.frame = CGRectMake(0, kViewHeight-bottomViewH-inset.bottom, kViewWidth, kItemHeight);
  248. _cancelBtn.frame = CGRectMake(10+inset.left, 7, GetMatchValue(GetLocalLanguageTextValue(ZLPhotoBrowserCancelText), 15, YES, bottomBtnH), bottomBtnH);
  249. _doneBtn.frame = CGRectMake(kViewWidth-70-inset.right, 7, 60, bottomBtnH);
  250. }
  251. #pragma mark - notifies
  252. //设备旋转
  253. - (void)deviceOrientationChanged:(NSNotification *)notify
  254. {
  255. _offsetX = self.collectionView.contentOffset.x + self.collectionView.contentInset.left;
  256. _orientationChanged = YES;
  257. }
  258. - (void)appResignActive
  259. {
  260. [self stopTimer];
  261. }
  262. - (void)appBecomeActive
  263. {
  264. [self startTimer];
  265. }
  266. - (void)setupUI
  267. {
  268. //禁用返回手势
  269. if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
  270. self.navigationController.interactivePopGestureRecognizer.delegate = nil;
  271. self.navigationController.interactivePopGestureRecognizer.enabled = NO;
  272. }
  273. self.view.backgroundColor = [UIColor blackColor];
  274. self.playerLayer = [[AVPlayerLayer alloc] init];
  275. [self.view.layer addSublayer:self.playerLayer];
  276. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  277. layout.itemSize = CGSizeMake(kItemWidth, kItemHeight);
  278. layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  279. layout.minimumInteritemSpacing = 0;
  280. layout.minimumLineSpacing = 0;
  281. self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
  282. self.collectionView.delegate = self;
  283. self.collectionView.dataSource = self;
  284. self.collectionView.backgroundColor = [UIColor clearColor];
  285. self.collectionView.showsHorizontalScrollIndicator = NO;
  286. [self.collectionView registerClass:ZLEditVideoCell.class forCellWithReuseIdentifier:@"ZLEditVideoCell"];
  287. [self.view addSubview:self.collectionView];
  288. [self creatBottomView];
  289. self.editView = [[ZLEditFrameView alloc] init];
  290. self.editView.delegate = self;
  291. [self.view addSubview:self.editView];
  292. _indicatorLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2, kItemHeight)];
  293. _indicatorLine.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:.7];
  294. }
  295. - (void)creatBottomView
  296. {
  297. ZLPhotoConfiguration *configuration = [(ZLImageNavigationController *)self.navigationController configuration];
  298. //下方视图
  299. _bottomView = [[UIView alloc] init];
  300. _bottomView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.7];
  301. [self.view addSubview:_bottomView];
  302. _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  303. _cancelBtn.titleLabel.font = [UIFont systemFontOfSize:15];
  304. [_cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  305. [_cancelBtn setTitle:GetLocalLanguageTextValue(ZLPhotoBrowserCancelText) forState:UIControlStateNormal];
  306. [_cancelBtn addTarget:self action:@selector(cancelBtn_click) forControlEvents:UIControlEventTouchUpInside];
  307. [_bottomView addSubview:_cancelBtn];
  308. _doneBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  309. [_doneBtn setTitle:GetLocalLanguageTextValue(ZLPhotoBrowserDoneText) forState:UIControlStateNormal];
  310. [_doneBtn setBackgroundColor:configuration.bottomBtnsNormalTitleColor];
  311. [_doneBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  312. _doneBtn.titleLabel.font = [UIFont systemFontOfSize:15];
  313. _doneBtn.layer.masksToBounds = YES;
  314. _doneBtn.layer.cornerRadius = 3.0f;
  315. [_doneBtn addTarget:self action:@selector(btnDone_click) forControlEvents:UIControlEventTouchUpInside];
  316. [_bottomView addSubview:_doneBtn];
  317. }
  318. #pragma mark - 解析视频每一帧图片
  319. - (void)analysisAssetImages
  320. {
  321. float duration = roundf(self.model.asset.duration);
  322. ZLPhotoConfiguration *configuration = [(ZLImageNavigationController *)self.navigationController configuration];
  323. _interval = configuration.maxEditVideoTime/10.0;
  324. _measureCount = (NSInteger)(duration / _interval);
  325. zl_weakify(self);
  326. [ZLPhotoManager requestVideoForAsset:self.model.asset completion:^(AVPlayerItem *item, NSDictionary *info) {
  327. dispatch_async(dispatch_get_main_queue(), ^{
  328. zl_strongify(weakSelf);
  329. if (!item) return;
  330. AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
  331. strongSelf.playerLayer.player = player;
  332. [strongSelf startTimer];
  333. });
  334. }];
  335. PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init];
  336. options.version = PHVideoRequestOptionsVersionOriginal;
  337. options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
  338. options.networkAccessAllowed = YES;
  339. [[PHImageManager defaultManager] requestAVAssetForVideo:self.model.asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
  340. zl_strongify(weakSelf);
  341. strongSelf->_avAsset = asset;
  342. dispatch_async(dispatch_get_main_queue(), ^{
  343. [strongSelf.collectionView reloadData];
  344. });
  345. }];
  346. }
  347. #pragma mark - action
  348. - (void)cancelBtn_click
  349. {
  350. [self stopTimer];
  351. ZLImageNavigationController *nav = (ZLImageNavigationController *)self.navigationController;
  352. ZLPhotoConfiguration *configuration = nav.configuration;
  353. if (configuration.editAfterSelectThumbnailImage &&
  354. configuration.maxSelectCount == 1) {
  355. [nav.arrSelectedModels removeAllObjects];
  356. }
  357. UIViewController *vc = [self.navigationController popViewControllerAnimated:NO];
  358. if (!vc) {
  359. [self dismissViewControllerAnimated:YES completion:nil];
  360. }
  361. }
  362. - (void)btnDone_click
  363. {
  364. [self stopTimer];
  365. ZLProgressHUD *hud = [[ZLProgressHUD alloc] init];
  366. [hud show];
  367. ZLImageNavigationController *nav = (ZLImageNavigationController *)self.navigationController;
  368. zl_weakify(self);
  369. __weak typeof(nav) weakNav = nav;
  370. [ZLPhotoManager exportEditVideoForAsset:_avAsset range:[self getTimeRange] type:nav.configuration.exportVideoType complete:^(BOOL isSuc, PHAsset *asset) {
  371. [hud hide];
  372. if (isSuc) {
  373. __strong typeof(weakNav) strongNav = weakNav;
  374. ZLPhotoModel *model = [ZLPhotoModel modelWithAsset:asset type:ZLAssetMediaTypeVideo duration:nil];
  375. [strongNav.arrSelectedModels removeAllObjects];
  376. [strongNav.arrSelectedModels addObject:model];
  377. if (strongNav.callSelectImageBlock) {
  378. strongNav.callSelectImageBlock();
  379. }
  380. } else {
  381. zl_strongify(weakSelf);
  382. [strongSelf startTimer];
  383. ShowToastLong(@"%@", GetLocalLanguageTextValue(ZLPhotoBrowserSaveVideoFailed));
  384. }
  385. }];
  386. }
  387. #pragma mark - timer
  388. - (void)startTimer
  389. {
  390. [self stopTimer];
  391. CGFloat duration = _interval * self.editView.validRect.size.width / (kItemWidth);
  392. _timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(playPartVideo:) userInfo:nil repeats:YES];
  393. [_timer fire];
  394. [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  395. _indicatorLine.frame = CGRectMake(self.editView.validRect.origin.x, 0, 2, kItemHeight);
  396. [self.editView addSubview:_indicatorLine];
  397. [UIView animateWithDuration:duration delay:.0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear animations:^{
  398. _indicatorLine.frame = CGRectMake(CGRectGetMaxX(self.editView.validRect)-2, 0, 2, kItemHeight);
  399. } completion:nil];
  400. }
  401. - (void)stopTimer
  402. {
  403. [_timer invalidate];
  404. _timer = nil;
  405. [_indicatorLine removeFromSuperview];
  406. [self.playerLayer.player pause];
  407. }
  408. - (CMTime)getStartTime
  409. {
  410. CGRect rect = [self.collectionView convertRect:self.editView.validRect fromView:self.editView];
  411. CGFloat s = MAX(0, _interval * rect.origin.x / (kItemWidth));
  412. return CMTimeMakeWithSeconds(s, self.playerLayer.player.currentTime.timescale);
  413. }
  414. - (CMTimeRange)getTimeRange
  415. {
  416. CMTime start = [self getStartTime];
  417. CGFloat d = _interval * self.editView.validRect.size.width / (kItemWidth);
  418. CMTime duration = CMTimeMakeWithSeconds(d, self.playerLayer.player.currentTime.timescale);
  419. return CMTimeRangeMake(start, duration);
  420. }
  421. - (void)playPartVideo:(NSTimer *)timer
  422. {
  423. [self.playerLayer.player play];
  424. [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  425. }
  426. #pragma mark - edit view delegate
  427. - (void)editViewValidRectChanged
  428. {
  429. [self stopTimer];
  430. [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  431. }
  432. - (void)editViewValidRectEndChanged
  433. {
  434. [self startTimer];
  435. }
  436. #pragma mark - scroll view delegate
  437. - (void)scrollViewDidScroll:(UIScrollView *)scrollView
  438. {
  439. if (!self.playerLayer.player || _orientationChanged) {
  440. _orientationChanged = NO;
  441. return;
  442. }
  443. [self stopTimer];
  444. [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  445. }
  446. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
  447. {
  448. if (!decelerate) {
  449. [self startTimer];
  450. }
  451. }
  452. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
  453. {
  454. [self startTimer];
  455. }
  456. #pragma mark - collection view data sources
  457. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  458. {
  459. return _measureCount;
  460. }
  461. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
  462. {
  463. ZLEditVideoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ZLEditVideoCell" forIndexPath:indexPath];
  464. UIImage *image = _imageCache[@(indexPath.row).stringValue];
  465. if (image) {
  466. cell.imageView.image = image;
  467. }
  468. return cell;
  469. }
  470. static const char _ZLOperationCellKey;
  471. - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
  472. {
  473. if (!_avAsset) return;
  474. if (_imageCache[@(indexPath.row).stringValue] || _opCache[@(indexPath.row).stringValue]) {
  475. return;
  476. }
  477. NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
  478. NSInteger row = indexPath.row;
  479. NSInteger i = row * _interval;
  480. CMTime time = CMTimeMake((i+0.35) * _avAsset.duration.timescale, _avAsset.duration.timescale);
  481. NSError *error = nil;
  482. CGImageRef cgImg = [self.generator copyCGImageAtTime:time actualTime:NULL error:&error];
  483. if (!error && cgImg) {
  484. UIImage *image = [UIImage imageWithCGImage:cgImg];
  485. CGImageRelease(cgImg);
  486. [_imageCache setValue:image forKey:@(row).stringValue];
  487. dispatch_async(dispatch_get_main_queue(), ^{
  488. NSIndexPath *nowIndexPath = [collectionView indexPathForCell:cell];
  489. if (row == nowIndexPath.row) {
  490. [(ZLEditVideoCell *)cell imageView].image = image;
  491. } else {
  492. UIImage *cacheImage = _imageCache[@(nowIndexPath.row).stringValue];
  493. if (cacheImage) {
  494. [(ZLEditVideoCell *)cell imageView].image = cacheImage;
  495. }
  496. }
  497. });
  498. [_opCache removeObjectForKey:@(row).stringValue];
  499. }
  500. objc_removeAssociatedObjects(cell);
  501. }];
  502. [_queue addOperation:op];
  503. [_opCache setValue:op forKey:@(indexPath.row).stringValue];
  504. objc_setAssociatedObject(cell, &_ZLOperationCellKey, op, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  505. }
  506. - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
  507. {
  508. NSBlockOperation *op = objc_getAssociatedObject(cell, &_ZLOperationCellKey);
  509. if (op) {
  510. [op cancel];
  511. objc_removeAssociatedObjects(cell);
  512. [_opCache removeObjectForKey:@(indexPath.row).stringValue];
  513. }
  514. }
  515. - (void)didReceiveMemoryWarning {
  516. [super didReceiveMemoryWarning];
  517. // Dispose of any resources that can be recreated.
  518. }
  519. /*
  520. #pragma mark - Navigation
  521. // In a storyboard-based application, you will often want to do a little preparation before navigation
  522. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  523. // Get the new view controller using [segue destinationViewController].
  524. // Pass the selected object to the new view controller.
  525. }
  526. */
  527. @end