NCZoomDismissalInteractionController.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import UIKit
  2. class ZoomDismissalInteractionController: NSObject {
  3. var transitionContext: UIViewControllerContextTransitioning?
  4. var animator: UIViewControllerAnimatedTransitioning?
  5. var fromReferenceImageViewFrame: CGRect?
  6. var toReferenceImageViewFrame: CGRect?
  7. func didPanWith(gestureRecognizer: UIPanGestureRecognizer) {
  8. guard let transitionContext = self.transitionContext,
  9. let animator = self.animator as? ZoomAnimator,
  10. let transitionImageView = animator.transitionImageView,
  11. let fromVC = transitionContext.viewController(forKey: .from),
  12. let toVC = transitionContext.viewController(forKey: .to),
  13. let fromReferenceImageView = animator.fromDelegate?.referenceImageView(for: animator),
  14. let toReferenceImageView = animator.toDelegate?.referenceImageView(for: animator),
  15. let fromReferenceImageViewFrame = self.fromReferenceImageViewFrame,
  16. let toReferenceImageViewFrame = self.toReferenceImageViewFrame else {
  17. return
  18. }
  19. fromReferenceImageView.isHidden = true
  20. let anchorPoint = CGPoint(x: fromReferenceImageViewFrame.midX, y: fromReferenceImageViewFrame.midY)
  21. let translatedPoint = gestureRecognizer.translation(in: fromReferenceImageView)
  22. let verticalDelta : CGFloat = translatedPoint.y < 0 ? 0 : translatedPoint.y
  23. let backgroundAlpha = backgroundAlphaFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)
  24. let scale = scaleFor(view: fromVC.view, withPanningVerticalDelta: verticalDelta)
  25. fromVC.view.alpha = backgroundAlpha
  26. transitionImageView.transform = CGAffineTransform(scaleX: scale, y: scale)
  27. let newCenter = CGPoint(x: anchorPoint.x + translatedPoint.x, y: anchorPoint.y + translatedPoint.y - transitionImageView.frame.height * (1 - scale) / 2.0)
  28. transitionImageView.center = newCenter
  29. toReferenceImageView.isHidden = true
  30. transitionContext.updateInteractiveTransition(1 - scale)
  31. toVC.tabBarController?.tabBar.alpha = 1 - backgroundAlpha
  32. if gestureRecognizer.state == .ended {
  33. let velocity = gestureRecognizer.velocity(in: fromVC.view)
  34. if velocity.y < 0 || newCenter.y < anchorPoint.y {
  35. //cancel
  36. UIView.animate(
  37. withDuration: 0.5,
  38. delay: 0,
  39. usingSpringWithDamping: 0.9,
  40. initialSpringVelocity: 0,
  41. options: [],
  42. animations: {
  43. transitionImageView.frame = fromReferenceImageViewFrame
  44. fromVC.view.alpha = 1.0
  45. toVC.tabBarController?.tabBar.alpha = 0
  46. },
  47. completion: { completed in
  48. toReferenceImageView.isHidden = false
  49. fromReferenceImageView.isHidden = false
  50. transitionImageView.removeFromSuperview()
  51. animator.transitionImageView = nil
  52. transitionContext.cancelInteractiveTransition()
  53. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  54. animator.toDelegate?.transitionDidEndWith(zoomAnimator: animator)
  55. animator.fromDelegate?.transitionDidEndWith(zoomAnimator: animator)
  56. self.transitionContext = nil
  57. })
  58. return
  59. }
  60. //start animation
  61. let finalTransitionSize = toReferenceImageViewFrame
  62. UIView.animate(withDuration: 0.25,
  63. delay: 0,
  64. options: [],
  65. animations: {
  66. fromVC.view.alpha = 0
  67. transitionImageView.frame = finalTransitionSize
  68. toVC.tabBarController?.tabBar.alpha = 1
  69. }, completion: { completed in
  70. transitionImageView.removeFromSuperview()
  71. toReferenceImageView.isHidden = false
  72. fromReferenceImageView.isHidden = false
  73. self.transitionContext?.finishInteractiveTransition()
  74. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  75. animator.toDelegate?.transitionDidEndWith(zoomAnimator: animator)
  76. animator.fromDelegate?.transitionDidEndWith(zoomAnimator: animator)
  77. self.transitionContext = nil
  78. })
  79. }
  80. }
  81. func backgroundAlphaFor(view: UIView, withPanningVerticalDelta verticalDelta: CGFloat) -> CGFloat {
  82. let startingAlpha:CGFloat = 1.0
  83. let finalAlpha: CGFloat = 0.0
  84. let totalAvailableAlpha = startingAlpha - finalAlpha
  85. let maximumDelta = view.bounds.height / 4.0
  86. let deltaAsPercentageOfMaximun = min(abs(verticalDelta) / maximumDelta, 1.0)
  87. return startingAlpha - (deltaAsPercentageOfMaximun * totalAvailableAlpha)
  88. }
  89. func scaleFor(view: UIView, withPanningVerticalDelta verticalDelta: CGFloat) -> CGFloat {
  90. let startingScale:CGFloat = 1.0
  91. let finalScale: CGFloat = 0.5
  92. let totalAvailableScale = startingScale - finalScale
  93. let maximumDelta = view.bounds.height / 2.0
  94. let deltaAsPercentageOfMaximun = min(abs(verticalDelta) / maximumDelta, 1.0)
  95. return startingScale - (deltaAsPercentageOfMaximun * totalAvailableScale)
  96. }
  97. }
  98. extension ZoomDismissalInteractionController: UIViewControllerInteractiveTransitioning {
  99. func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
  100. self.transitionContext = transitionContext
  101. let containerView = transitionContext.containerView
  102. guard let animator = self.animator as? ZoomAnimator,
  103. let fromVC = transitionContext.viewController(forKey: .from),
  104. let toVC = transitionContext.viewController(forKey: .to),
  105. let fromReferenceImageViewFrame = animator.fromDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
  106. let toReferenceImageViewFrame = animator.toDelegate?.referenceImageViewFrameInTransitioningView(for: animator),
  107. let fromReferenceImageView = animator.fromDelegate?.referenceImageView(for: animator)
  108. else {
  109. return
  110. }
  111. animator.fromDelegate?.transitionWillStartWith(zoomAnimator: animator)
  112. animator.toDelegate?.transitionWillStartWith(zoomAnimator: animator)
  113. self.fromReferenceImageViewFrame = fromReferenceImageViewFrame
  114. self.toReferenceImageViewFrame = toReferenceImageViewFrame
  115. let referenceImage = fromReferenceImageView.image!
  116. containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
  117. if animator.transitionImageView == nil {
  118. let transitionImageView = UIImageView(image: referenceImage)
  119. transitionImageView.contentMode = .scaleAspectFill
  120. transitionImageView.clipsToBounds = true
  121. transitionImageView.frame = fromReferenceImageViewFrame
  122. animator.transitionImageView = transitionImageView
  123. containerView.addSubview(transitionImageView)
  124. }
  125. }
  126. }