HXPhotoInteractiveTransition.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. //
  2. // HXPhotoInteractiveTransition.m
  3. // HXPhotoPickerExample
  4. //
  5. // Created by Silence on 2017/10/28.
  6. // Copyright © 2017年 Silence. All rights reserved.
  7. //
  8. #import "HXPhotoInteractiveTransition.h"
  9. #import "HXPhotoPreviewViewController.h"
  10. #import "HXPhotoViewController.h"
  11. #import "HXPhotoPreviewBottomView.h"
  12. @interface HXPhotoInteractiveTransition ()<UIGestureRecognizerDelegate>
  13. @property (nonatomic, weak) id<UIViewControllerContextTransitioning> transitionContext;
  14. @property (nonatomic, weak) HXPhotoPreviewViewController *vc;
  15. @property (nonatomic, weak) HXPhotoViewController *toVC;
  16. @property (strong, nonatomic) UIView *bgView;
  17. @property (weak, nonatomic) HXPhotoViewCell *tempCell;
  18. @property (weak, nonatomic) HXPhotoPreviewViewCell *fromCell;
  19. @property (assign, nonatomic) CGRect imageInitialFrame;
  20. @property (assign, nonatomic) CGPoint transitionImgViewCenter;
  21. @property (assign, nonatomic) CGFloat beginX;
  22. @property (assign, nonatomic) CGFloat beginY;
  23. @property (assign, nonatomic) CGPoint slidingGap;
  24. @property (strong, nonatomic) UIPanGestureRecognizer *panGesture;
  25. @property (assign, nonatomic) BOOL startPop;
  26. @property (assign, nonatomic) BOOL isFinished;
  27. @property (assign, nonatomic) BOOL beginInterPercentCompletion;
  28. @end
  29. @implementation HXPhotoInteractiveTransition
  30. - (void)addPanGestureForViewController:(UIViewController *)viewController {
  31. self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognizeDidUpdate:)];
  32. self.slidingGap = CGPointZero;
  33. self.panGesture.delegate = self;
  34. self.vc = (HXPhotoPreviewViewController *)viewController;
  35. [viewController.view addGestureRecognizer:self.panGesture];
  36. }
  37. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
  38. return !self.vc.collectionView.isDragging;
  39. }
  40. - (void)gestureRecognizeDidUpdate:(UIPanGestureRecognizer *)gestureRecognizer {
  41. if (self.isFinished) {
  42. return;
  43. }
  44. BOOL isTracking = NO;
  45. HXPhotoPreviewViewCell *cell = [self.vc currentPreviewCell];
  46. CGRect toRect = [cell.previewContentView convertRect:cell.previewContentView.bounds toView:cell.scrollView];
  47. if ((cell.scrollView.isZooming || cell.scrollView.contentOffset.y > 0 || cell.scrollView.isZoomBouncing || !cell.allowInteration || (toRect.origin.x != 0 && cell.previewContentView.hx_w > cell.scrollView.hx_w)) && !self.interation) {
  48. return;
  49. }else {
  50. isTracking = cell.scrollView.isTracking;
  51. }
  52. switch (gestureRecognizer.state) {
  53. case UIGestureRecognizerStateBegan: {
  54. [self panGestureBegan:gestureRecognizer];
  55. } break;
  56. case UIGestureRecognizerStateChanged:
  57. if (!self.interation || !self.beginInterPercentCompletion) {
  58. if (isTracking) {
  59. [self panGestureBegan:gestureRecognizer];
  60. if (self.interation) {
  61. self.slidingGap = [gestureRecognizer translationInView:gestureRecognizer.view];
  62. }
  63. }
  64. return;
  65. }
  66. [self panGestureChanged:gestureRecognizer];
  67. break;
  68. case UIGestureRecognizerStateEnded:
  69. self.startPop = NO;
  70. [self panGestureEnd:gestureRecognizer];
  71. self.slidingGap = CGPointZero;
  72. break;
  73. default:
  74. self.startPop = NO;
  75. [self panGestureOther:gestureRecognizer];
  76. self.slidingGap = CGPointZero;
  77. break;
  78. }
  79. }
  80. - (CGFloat)panGestureScale:(UIPanGestureRecognizer *)panGesture {
  81. CGPoint translation = [panGesture translationInView:panGesture.view];
  82. CGFloat transitionY = translation.y;
  83. CGFloat scale = (transitionY - self.slidingGap.y) / ((panGesture.view.frame.size.height - 50) / 2);
  84. if (scale > 1.f) {
  85. scale = 1.f;
  86. }
  87. return scale;
  88. }
  89. - (void)panGestureBegan:(UIPanGestureRecognizer *)panGesture {
  90. if (self.interation || self.beginInterPercentCompletion || self.startPop) {
  91. return;
  92. }
  93. HXPhotoPreviewViewController *previewVC = (HXPhotoPreviewViewController *)self.vc;
  94. CGPoint velocity = [panGesture velocityInView:previewVC.view];
  95. BOOL isVerticalGesture = (fabs(velocity.y) > fabs(velocity.x) && velocity.y > 0);
  96. if (!isVerticalGesture) {
  97. return;
  98. }
  99. [previewVC setStopCancel:YES];
  100. self.beginX = [panGesture locationInView:panGesture.view].x;
  101. self.beginY = [panGesture locationInView:panGesture.view].y;
  102. self.interation = YES;
  103. self.beginInterPercentCompletion = NO;
  104. self.startPop = YES;
  105. [self.vc.navigationController popViewControllerAnimated:YES];
  106. }
  107. - (void)panGestureChanged:(UIPanGestureRecognizer *)panGesture {
  108. if (self.interation && self.beginInterPercentCompletion) {
  109. CGFloat scale = [self panGestureScale:panGesture];
  110. if (scale < 0.f) {
  111. scale = 0.f;
  112. }
  113. CGPoint translation = [panGesture translationInView:panGesture.view];
  114. CGFloat imageViewScale = 1 - scale * 0.5;
  115. if (imageViewScale < 0.4) {
  116. imageViewScale = 0.4;
  117. }
  118. self.fromCell.center = CGPointMake(self.transitionImgViewCenter.x + (translation.x - self.slidingGap.x), self.transitionImgViewCenter.y + (translation.y - self.slidingGap.y));
  119. self.fromCell.transform = CGAffineTransformMakeScale(imageViewScale, imageViewScale);
  120. [self updateInterPercent:1 - scale * scale];
  121. [self updateInteractiveTransition:scale];
  122. }
  123. }
  124. - (void)panGestureEnd:(UIPanGestureRecognizer *)panGesture {
  125. if (self.interation) {
  126. CGFloat scale = [self panGestureScale:panGesture];
  127. if (scale < 0.f) {
  128. scale = 0.f;
  129. }
  130. if (scale < 0.15f){
  131. [self cancelInteractiveTransition];
  132. [self interPercentCancel];
  133. }else {
  134. self.isFinished = YES;
  135. [self finishInteractiveTransition];
  136. [self interPercentFinish];
  137. }
  138. }
  139. }
  140. - (void)panGestureOther:(UIPanGestureRecognizer *)panGesture {
  141. self.vc.view.userInteractionEnabled = YES;
  142. if (self.interation) {
  143. [self cancelInteractiveTransition];
  144. [self interPercentCancel];
  145. }
  146. }
  147. - (void)beginInterPercent {
  148. id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
  149. HXPhotoPreviewViewController *fromVC = (HXPhotoPreviewViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  150. HXPhotoViewController *toVC = (HXPhotoViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  151. UIView *containerView = [transitionContext containerView];
  152. [containerView addSubview:toVC.view];
  153. [containerView addSubview:fromVC.view];
  154. if (!self.startPop) {
  155. self.interation = NO;
  156. [self.vc.view removeGestureRecognizer:self.panGesture];
  157. self.isFinished = YES;
  158. [self cancelInteractiveTransition];
  159. NSTimeInterval duration = fromVC.manager.configuration.popInteractiveTransitionDuration;
  160. UIViewAnimationOptions option = UIViewAnimationOptionLayoutSubviews;
  161. [UIView animateWithDuration:duration delay:0 options:option animations:^{
  162. } completion:^(BOOL finished) {
  163. [self.transitionContext completeTransition:NO];
  164. self.isFinished = NO;
  165. [self.vc.view addGestureRecognizer:self.panGesture];
  166. }];
  167. return;
  168. }
  169. HXPhotoModel *model = [fromVC.modelArray objectAtIndex:fromVC.currentModelIndex];
  170. HXPhotoPreviewViewCell *fromCell = [fromVC currentPreviewCell:model];
  171. HXPhotoViewCell *toCell = [toVC currentPreviewCell:model];
  172. self.fromCell = fromCell;
  173. CGRect tempImageViewFrame;
  174. self.imageInitialFrame = fromCell.frame;
  175. tempImageViewFrame = [fromCell convertRect:fromCell.bounds toView:containerView];
  176. if (!toCell) {
  177. [toVC scrollToModel:model];
  178. toCell = [toVC currentPreviewCell:model];
  179. }
  180. self.bgView = [[UIView alloc] initWithFrame:containerView.bounds];
  181. self.bgView.backgroundColor = [HXPhotoCommon photoCommon].isDark ? [UIColor blackColor] : fromVC.manager.configuration.previewPhotoViewBgColor;
  182. CGFloat scaleX;
  183. CGFloat scaleY;
  184. if (self.beginX < tempImageViewFrame.origin.x) {
  185. scaleX = 0;
  186. }else if (self.beginX > CGRectGetMaxX(tempImageViewFrame)) {
  187. scaleX = 1.0f;
  188. }else {
  189. scaleX = (self.beginX - tempImageViewFrame.origin.x) / tempImageViewFrame.size.width;
  190. }
  191. if (self.beginY < tempImageViewFrame.origin.y) {
  192. scaleY = 0;
  193. }else if (self.beginY > CGRectGetMaxY(tempImageViewFrame)){
  194. scaleY = 1.0f;
  195. }else {
  196. scaleY = (self.beginY - tempImageViewFrame.origin.y) / tempImageViewFrame.size.height;
  197. }
  198. self.fromCell.layer.anchorPoint = CGPointMake(scaleX, scaleY);
  199. self.fromCell.frame = tempImageViewFrame;
  200. self.transitionImgViewCenter = self.fromCell.center;
  201. [toVC.view insertSubview:self.bgView belowSubview:toVC.bottomView];
  202. [toVC.view insertSubview:self.fromCell belowSubview:toVC.bottomView];
  203. if (!fromVC.bottomView.userInteractionEnabled) {
  204. self.bgView.backgroundColor = [UIColor blackColor];
  205. #pragma clang diagnostic push
  206. #pragma clang diagnostic ignored"-Wdeprecated-declarations"
  207. [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
  208. #pragma clang diagnostic pop
  209. if (HX_IOS11_Later) {
  210. // 处理 ios11 当导航栏隐藏时手势返回的问题
  211. [toVC.navigationController.navigationBar.layer removeAllAnimations];
  212. // 找到动画异常的视图,然后移除layer动画 !!!!!
  213. // 一层一层的慢慢的找,把每个有动画的全部移除
  214. [self removeAllAnimationsForView:toVC.navigationController.navigationBar];
  215. [toVC.navigationController setNavigationBarHidden:NO animated:YES];
  216. }else {
  217. [toVC.navigationController setNavigationBarHidden:NO];
  218. }
  219. toVC.navigationController.navigationBar.alpha = 0;
  220. toVC.bottomView.alpha = 0;
  221. toVC.limitView.alpha = 0;
  222. }else {
  223. toVC.limitView.alpha = 1;
  224. toVC.bottomView.alpha = 1;
  225. self.bgView.backgroundColor = [HXPhotoCommon photoCommon].isDark ? [UIColor blackColor] : fromVC.manager.configuration.previewPhotoViewBgColor;
  226. }
  227. // toVC.navigationController.navigationBar.userInteractionEnabled = NO;
  228. self.toVC = toVC;
  229. fromVC.collectionView.hidden = YES;
  230. toCell.hidden = YES;
  231. fromVC.view.backgroundColor = [UIColor clearColor];
  232. CGRect rect = [toCell.imageView convertRect:toCell.imageView.bounds toView: containerView];
  233. if (toCell) {
  234. [toVC scrollToPoint:toCell rect:rect];
  235. }
  236. self.tempCell = toCell;
  237. if (self.fromCell.previewContentView.model.subType == HXPhotoModelMediaSubTypeVideo) {
  238. [self.fromCell.previewContentView.videoView hideOtherView:YES];
  239. }
  240. [self resetScrollView:NO];
  241. self.beginInterPercentCompletion = YES;
  242. }
  243. - (void)removeAllAnimationsForView:(UIView *)view {
  244. for (UIView *navBarView in view.subviews) {
  245. [navBarView.layer removeAllAnimations];
  246. for (UIView *navBarSubView in navBarView.subviews) {
  247. [navBarSubView.layer removeAllAnimations];
  248. for (UIView *backView in navBarSubView.subviews) {
  249. [backView.layer removeAllAnimations];
  250. for (UIView *backSubView in backView.subviews) {
  251. [backSubView.layer removeAllAnimations];
  252. for (UIView *backSSubView in backSubView.subviews) {
  253. [backSSubView.layer removeAllAnimations];
  254. for (CALayer *subLayer in backSSubView.layer.sublayers) {
  255. // !!!!!!!!
  256. [subLayer removeAllAnimations];
  257. }
  258. }
  259. }
  260. }
  261. }
  262. }
  263. }
  264. - (void)updateInterPercent:(CGFloat)scale{
  265. HXPhotoPreviewViewController *fromVC = (HXPhotoPreviewViewController *)[self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  266. fromVC.view.alpha = scale;
  267. self.bgView.alpha = fromVC.view.alpha;
  268. if (!fromVC.bottomView.userInteractionEnabled) {
  269. HXPhotoViewController *toVC = (HXPhotoViewController *)[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  270. toVC.bottomView.alpha = 1 - scale;
  271. toVC.limitView.alpha = 1 - scale;
  272. toVC.navigationController.navigationBar.alpha = 1 - scale;
  273. }
  274. }
  275. - (void)interPercentCancel{
  276. id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
  277. HXPhotoPreviewViewController *fromVC = (HXPhotoPreviewViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  278. HXPhotoViewController *toVC = (HXPhotoViewController *)[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  279. if (!fromVC.bottomView.userInteractionEnabled) {
  280. #pragma clang diagnostic push
  281. #pragma clang diagnostic ignored"-Wdeprecated-declarations"
  282. [[UIApplication sharedApplication] setStatusBarHidden:YES];
  283. #pragma clang diagnostic pop
  284. [toVC.navigationController setNavigationBarHidden:YES];
  285. toVC.navigationController.navigationBar.alpha = 1;
  286. }
  287. if (self.fromCell.previewContentView.model.subType == HXPhotoModelMediaSubTypeVideo) {
  288. [self.fromCell.previewContentView.videoView showOtherView];
  289. }
  290. self.panGesture.enabled = NO;
  291. [UIView animateWithDuration:0.2f animations:^{
  292. fromVC.view.alpha = 1;
  293. self.fromCell.transform = CGAffineTransformIdentity;
  294. self.fromCell.center = self.transitionImgViewCenter;
  295. self.bgView.alpha = 1;
  296. if (!fromVC.bottomView.userInteractionEnabled) {
  297. toVC.bottomView.alpha = 0;
  298. toVC.limitView.alpha = 0;
  299. }else {
  300. toVC.bottomView.alpha = 1;
  301. toVC.limitView.alpha = 1;
  302. }
  303. } completion:^(BOOL finished) {
  304. // toVC.navigationController.navigationBar.userInteractionEnabled = YES;
  305. if (finished) {
  306. fromVC.collectionView.hidden = NO;
  307. if (!fromVC.bottomView.userInteractionEnabled) {
  308. fromVC.view.backgroundColor = [UIColor blackColor];
  309. if (HX_IOS11_Later) {
  310. // 处理 ios11 当导航栏隐藏时手势返回的问题
  311. [toVC.navigationController setNavigationBarHidden:YES];
  312. }
  313. }else {
  314. fromVC.view.backgroundColor = [HXPhotoCommon photoCommon].isDark ? [UIColor blackColor] : fromVC.manager.configuration.previewPhotoViewBgColor;
  315. }
  316. self.tempCell.hidden = NO;
  317. self.tempCell = nil;
  318. [self resetScrollView:YES];
  319. self.fromCell.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
  320. self.fromCell.frame = self.imageInitialFrame;
  321. [fromVC.collectionView addSubview:self.fromCell];
  322. [self.bgView removeFromSuperview];
  323. self.bgView = nil;
  324. self.fromCell = nil;
  325. self.toVC = nil;
  326. [self.transitionContext completeTransition:NO];
  327. self.transitionContext = nil;
  328. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  329. self.panGesture.enabled = YES;
  330. });
  331. self.interation = NO;
  332. self.beginInterPercentCompletion = NO;
  333. }
  334. }];
  335. }
  336. //完成
  337. - (void)interPercentFinish {
  338. id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
  339. UIView *containerView = [transitionContext containerView];
  340. HXPhotoPreviewViewController *fromVC = (HXPhotoPreviewViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  341. HXPhotoViewController *toVC = (HXPhotoViewController *)[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  342. NSTimeInterval duration = fromVC.manager.configuration.popInteractiveTransitionDuration;
  343. UIViewAnimationOptions option = UIViewAnimationOptionLayoutSubviews;
  344. if (self.fromCell.previewContentView.model.subType == HXPhotoModelMediaSubTypeVideo) {
  345. [self.fromCell.previewContentView.videoView hideOtherView:NO];
  346. }
  347. CGRect toRect = [self.tempCell convertRect:self.tempCell.bounds toView:containerView];
  348. [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.8 initialSpringVelocity:0.1 options:option animations:^{
  349. if (self.tempCell) {
  350. self.fromCell.transform = CGAffineTransformIdentity;
  351. self.fromCell.frame = toRect;
  352. self.fromCell.scrollView.contentOffset = CGPointZero;
  353. self.fromCell.previewContentView.frame = CGRectMake(0, 0, toRect.size.width, toRect.size.height);
  354. }else {
  355. self.fromCell.center = self.transitionImgViewCenter;
  356. self.fromCell.alpha = 0;
  357. self.fromCell.transform = CGAffineTransformMakeScale(0.3, 0.3);
  358. }
  359. fromVC.view.alpha = 0;
  360. self.bgView.alpha = 0;
  361. toVC.navigationController.navigationBar.alpha = 1;
  362. toVC.bottomView.alpha = 1;
  363. toVC.limitView.alpha = 1;
  364. }completion:^(BOOL finished) {
  365. // toVC.navigationController.navigationBar.userInteractionEnabled = YES;
  366. if (finished) {
  367. if (!fromVC.bottomView.userInteractionEnabled && HX_IOS11_Later) {
  368. // 处理 ios11 当导航栏隐藏时手势返回的问题
  369. [toVC.navigationController.navigationBar.layer removeAllAnimations];
  370. [toVC.navigationController setNavigationBarHidden:NO];
  371. }
  372. [self.tempCell bottomViewPrepareAnimation];
  373. self.tempCell.hidden = NO;
  374. [self.tempCell bottomViewStartAnimation];
  375. [self.fromCell.previewContentView cancelRequest];
  376. [self.fromCell removeFromSuperview];
  377. [self.bgView removeFromSuperview];
  378. self.fromCell = nil;
  379. self.toVC = nil;
  380. [self.transitionContext completeTransition:YES];
  381. self.transitionContext = nil;
  382. }
  383. self.interation = NO;
  384. self.beginInterPercentCompletion = NO;
  385. }];
  386. }
  387. - (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
  388. self.transitionContext = transitionContext;
  389. [self beginInterPercent];
  390. }
  391. - (void)resetScrollView:(BOOL)enabled {
  392. id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
  393. HXPhotoPreviewViewController *fromVC = (HXPhotoPreviewViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  394. fromVC.collectionView.scrollEnabled = enabled;
  395. self.fromCell.scrollView.scrollEnabled = enabled;
  396. self.fromCell.scrollView.pinchGestureRecognizer.enabled = enabled;
  397. self.fromCell.scrollView.clipsToBounds = enabled;
  398. }
  399. @end