NCZoomAnimator.swift 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import UIKit
  2. protocol ZoomAnimatorDelegate: class {
  3. func transitionWillStartWith(zoomAnimator: ZoomAnimator)
  4. func transitionDidEndWith(zoomAnimator: ZoomAnimator)
  5. func referenceImageView(for zoomAnimator: ZoomAnimator) -> UIImageView?
  6. func referenceImageViewFrameInTransitioningView(for zoomAnimator: ZoomAnimator) -> CGRect?
  7. }
  8. class ZoomAnimator: NSObject {
  9. weak var fromDelegate: ZoomAnimatorDelegate?
  10. weak var toDelegate: ZoomAnimatorDelegate?
  11. var transitionImageView: UIImageView?
  12. var isPresenting: Bool = true
  13. fileprivate func animateZoomInTransition(using transitionContext: UIViewControllerContextTransitioning) {
  14. let containerView = transitionContext.containerView
  15. guard let toVC = transitionContext.viewController(forKey: .to),
  16. let fromVC = transitionContext.viewController(forKey: .from),
  17. let fromReferenceImageView = self.fromDelegate?.referenceImageView(for: self),
  18. let toReferenceImageView = self.toDelegate?.referenceImageView(for: self),
  19. let fromReferenceImageViewFrame = self.fromDelegate?.referenceImageViewFrameInTransitioningView(for: self)
  20. else {
  21. return
  22. }
  23. self.fromDelegate?.transitionWillStartWith(zoomAnimator: self)
  24. self.toDelegate?.transitionWillStartWith(zoomAnimator: self)
  25. toVC.view.alpha = 0
  26. toReferenceImageView.isHidden = true
  27. containerView.addSubview(toVC.view)
  28. let referenceImage = fromReferenceImageView.image!
  29. if self.transitionImageView == nil {
  30. let transitionImageView = UIImageView(image: referenceImage)
  31. transitionImageView.contentMode = .scaleAspectFill
  32. transitionImageView.clipsToBounds = true
  33. transitionImageView.frame = fromReferenceImageViewFrame
  34. self.transitionImageView = transitionImageView
  35. containerView.addSubview(transitionImageView)
  36. }
  37. fromReferenceImageView.isHidden = true
  38. let finalTransitionSize = calculateZoomInImageFrame(image: referenceImage, forView: toVC.view)
  39. UIView.animate(withDuration: transitionDuration(using: transitionContext),
  40. delay: 0,
  41. usingSpringWithDamping: 0.8,
  42. initialSpringVelocity: 0,
  43. options: [UIView.AnimationOptions.transitionCrossDissolve],
  44. animations: {
  45. self.transitionImageView?.frame = finalTransitionSize
  46. toVC.view.alpha = 1.0
  47. fromVC.tabBarController?.tabBar.alpha = 0
  48. },
  49. completion: { completed in
  50. self.transitionImageView?.removeFromSuperview()
  51. toReferenceImageView.isHidden = false
  52. fromReferenceImageView.isHidden = false
  53. self.transitionImageView = nil
  54. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  55. self.toDelegate?.transitionDidEndWith(zoomAnimator: self)
  56. self.fromDelegate?.transitionDidEndWith(zoomAnimator: self)
  57. })
  58. }
  59. fileprivate func animateZoomOutTransition(using transitionContext: UIViewControllerContextTransitioning) {
  60. let containerView = transitionContext.containerView
  61. guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
  62. let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
  63. let fromReferenceImageView = self.fromDelegate?.referenceImageView(for: self),
  64. let toReferenceImageView = self.toDelegate?.referenceImageView(for: self),
  65. let fromReferenceImageViewFrame = self.fromDelegate?.referenceImageViewFrameInTransitioningView(for: self),
  66. let toReferenceImageViewFrame = self.toDelegate?.referenceImageViewFrameInTransitioningView(for: self)
  67. else {
  68. return
  69. }
  70. self.fromDelegate?.transitionWillStartWith(zoomAnimator: self)
  71. self.toDelegate?.transitionWillStartWith(zoomAnimator: self)
  72. toReferenceImageView.isHidden = true
  73. let referenceImage = fromReferenceImageView.image!
  74. if self.transitionImageView == nil {
  75. let transitionImageView = UIImageView(image: referenceImage)
  76. transitionImageView.contentMode = .scaleAspectFill
  77. transitionImageView.clipsToBounds = true
  78. transitionImageView.frame = fromReferenceImageViewFrame
  79. self.transitionImageView = transitionImageView
  80. containerView.addSubview(transitionImageView)
  81. }
  82. containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
  83. fromReferenceImageView.isHidden = true
  84. let finalTransitionSize = toReferenceImageViewFrame
  85. UIView.animate(withDuration: transitionDuration(using: transitionContext),
  86. delay: 0,
  87. options: [],
  88. animations: {
  89. fromVC.view.alpha = 0
  90. self.transitionImageView?.frame = finalTransitionSize
  91. toVC.tabBarController?.tabBar.alpha = 1
  92. }, completion: { completed in
  93. self.transitionImageView?.removeFromSuperview()
  94. toReferenceImageView.isHidden = false
  95. fromReferenceImageView.isHidden = false
  96. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  97. self.toDelegate?.transitionDidEndWith(zoomAnimator: self)
  98. self.fromDelegate?.transitionDidEndWith(zoomAnimator: self)
  99. })
  100. }
  101. private func calculateZoomInImageFrame(image: UIImage, forView view: UIView) -> CGRect {
  102. let viewRatio = view.frame.size.width / view.frame.size.height
  103. let imageRatio = image.size.width / image.size.height
  104. let touchesSides = (imageRatio > viewRatio)
  105. if touchesSides {
  106. let height = view.frame.width / imageRatio
  107. let yPoint = view.frame.minY + (view.frame.height - height) / 2
  108. return CGRect(x: 0, y: yPoint, width: view.frame.width, height: height)
  109. } else {
  110. let width = view.frame.height * imageRatio
  111. let xPoint = view.frame.minX + (view.frame.width - width) / 2
  112. return CGRect(x: xPoint, y: 0, width: width, height: view.frame.height)
  113. }
  114. }
  115. }
  116. extension ZoomAnimator: UIViewControllerAnimatedTransitioning {
  117. func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
  118. if self.isPresenting {
  119. return 0.5
  120. } else {
  121. return 0.25
  122. }
  123. }
  124. func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  125. if self.isPresenting {
  126. animateZoomInTransition(using: transitionContext)
  127. } else {
  128. animateZoomOutTransition(using: transitionContext)
  129. }
  130. }
  131. }