NatGeoAnimator.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. //
  2. // Created by Tom Baranes on 24/04/16.
  3. // Copyright © 2016 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. public class NatGeoAnimator: NSObject, AnimatedTransitioning {
  7. // MARK: - AnimatorProtocol
  8. public var transitionAnimationType: TransitionAnimationType
  9. public var transitionDuration: Duration = defaultTransitionDuration
  10. public var reverseAnimationType: TransitionAnimationType?
  11. public var interactiveGestureType: InteractiveGestureType?
  12. // MARK: - private
  13. fileprivate var fromDirection: TransitionAnimationType.Direction
  14. fileprivate let firstPartRatio: Double = 0.8
  15. // MARK: - Life cycle
  16. public init(from direction: TransitionAnimationType.Direction, duration: Duration) {
  17. transitionDuration = duration
  18. fromDirection = direction
  19. transitionAnimationType = .natGeo(to: direction)
  20. reverseAnimationType = .natGeo(to: direction.opposite)
  21. super.init()
  22. }
  23. }
  24. extension NatGeoAnimator: UIViewControllerAnimatedTransitioning {
  25. public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
  26. return retrieveTransitionDuration(transitionContext: transitionContext)
  27. }
  28. public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  29. let (tempfromView, tempToView, tempContainerView) = retrieveViews(transitionContext: transitionContext)
  30. guard let fromView = tempfromView,
  31. let toView = tempToView,
  32. let containerView = tempContainerView else {
  33. transitionContext.completeTransition(true)
  34. return
  35. }
  36. let (_, tempToViewController, _) = retrieveViewControllers(transitionContext: transitionContext)
  37. if let toViewController = tempToViewController {
  38. toView.frame = transitionContext.finalFrame(for: toViewController)
  39. }
  40. containerView.addSubview(toView)
  41. containerView.addSubview(fromView)
  42. if fromDirection == .left {
  43. executeLeftAnimation(context: transitionContext, containerView: containerView, fromView: fromView, toView: toView)
  44. } else {
  45. executeRightAnimations(context: transitionContext, containerView: containerView, fromView: fromView, toView: toView)
  46. }
  47. }
  48. }
  49. // MARK: - Left
  50. private extension NatGeoAnimator {
  51. func executeLeftAnimation(context transitionContext: UIViewControllerContextTransitioning,
  52. containerView: UIView,
  53. fromView: UIView,
  54. toView: UIView) {
  55. fromView.isUserInteractionEnabled = false
  56. var fromLayer = fromView.layer
  57. var toLayer = toView.layer
  58. let oldFrame = fromLayer.frame
  59. let oldCenter = fromView.center
  60. let oldAnchorPoint = fromLayer.anchorPoint
  61. fromLayer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
  62. fromLayer.frame = oldFrame
  63. sourceFirstTransform(&fromLayer)
  64. destinationFirstTransform(&toLayer)
  65. UIView.animateKeyframes(withDuration: transitionDuration, delay: 0.0, options: .calculationModeCubic, animations: {
  66. UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1.0) {
  67. self.destinationLastTransform(&toLayer)
  68. }
  69. UIView.addKeyframe(withRelativeStartTime: 1.0 - self.firstPartRatio, relativeDuration: self.firstPartRatio) {
  70. self.sourceLastTransform(&fromLayer)
  71. }
  72. }) { _ in
  73. if transitionContext.transitionWasCancelled {
  74. containerView.bringSubviewToFront(fromView)
  75. fromView.isUserInteractionEnabled = true
  76. }
  77. self.animationDidFinish(transitionContext, containerView: containerView, fromView: fromView, toView: toView)
  78. fromLayer.anchorPoint = oldAnchorPoint
  79. fromView.center = oldCenter
  80. }
  81. }
  82. }
  83. // MARK: - Right
  84. private extension NatGeoAnimator {
  85. func executeRightAnimations(context transitionContext: UIViewControllerContextTransitioning,
  86. containerView: UIView,
  87. fromView: UIView,
  88. toView: UIView) {
  89. toView.isUserInteractionEnabled = true
  90. var fromLayer = toView.layer
  91. var toLayer = fromView.layer
  92. let oldFrame = fromLayer.frame
  93. let oldCenter = toView.center
  94. let oldAnchorPoint = fromLayer.anchorPoint
  95. fromLayer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
  96. fromLayer.frame = oldFrame
  97. sourceLastTransform(&fromLayer)
  98. destinationLastTransform(&toLayer)
  99. UIView.animateKeyframes(withDuration: transitionDuration, delay: 0.0, options: .calculationModeCubic, animations: {
  100. UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: self.firstPartRatio) {
  101. self.sourceFirstTransform(&fromLayer)
  102. }
  103. UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1.0) {
  104. self.destinationFirstTransform(&toLayer)
  105. }
  106. }) { _ in
  107. if transitionContext.transitionWasCancelled {
  108. containerView.bringSubviewToFront(fromView)
  109. toView.isUserInteractionEnabled = false
  110. }
  111. self.animationDidFinish(transitionContext, containerView: containerView, fromView: fromView, toView: toView)
  112. fromLayer.anchorPoint = oldAnchorPoint
  113. toView.center = oldCenter
  114. }
  115. }
  116. }
  117. // MARK: - Helpers
  118. private extension NatGeoAnimator {
  119. func sourceFirstTransform(_ layer: inout CALayer) {
  120. var transform = CATransform3DIdentity
  121. transform.m34 = 1.0 / -500
  122. transform = CATransform3DTranslate(transform, 0.0, 0.0, 0.0)
  123. layer.transform = transform
  124. }
  125. func sourceLastTransform(_ layer: inout CALayer) {
  126. var transform = CATransform3DIdentity
  127. transform.m34 = 1.0 / -500.0
  128. transform = CATransform3DRotate(transform, radianFromDegree(80), 0.0, 1.0, 0.0)
  129. transform = CATransform3DTranslate(transform, 0.0, 0.0, -30.0)
  130. transform = CATransform3DTranslate(transform, 170.0, 0.0, 0.0)
  131. layer.transform = transform
  132. }
  133. func destinationFirstTransform(_ layer: inout CALayer) {
  134. var transform = CATransform3DIdentity
  135. transform.m34 = 1.0 / -500.0
  136. transform = CATransform3DRotate(transform, radianFromDegree(5.0), 0.0, 0.0, 1.0)
  137. transform = CATransform3DTranslate(transform, 320.0, -40.0, 150.0)
  138. transform = CATransform3DRotate(transform, radianFromDegree(-45), 0.0, 1.0, 0.0)
  139. transform = CATransform3DRotate(transform, radianFromDegree(10), 1.0, 0.0, 0.0)
  140. layer.transform = transform
  141. }
  142. func destinationLastTransform(_ layer: inout CALayer) {
  143. var transform = CATransform3DIdentity
  144. transform.m34 = 1.0 / -500
  145. transform = CATransform3DRotate(transform, radianFromDegree(0), 0.0, 0.0, 1.0)
  146. transform = CATransform3DTranslate(transform, 0.0, 0.0, 0.0)
  147. transform = CATransform3DRotate(transform, radianFromDegree(0), 0.0, 1.0, 0.0)
  148. transform = CATransform3DRotate(transform, radianFromDegree(0), 1.0, 0.0, 0.0)
  149. layer.transform = transform
  150. }
  151. func radianFromDegree(_ degrees: Double) -> CGFloat {
  152. return CGFloat((degrees / 180) * .pi)
  153. }
  154. func animationDidFinish(_ transitionContext: UIViewControllerContextTransitioning, containerView: UIView, fromView: UIView, toView: UIView) {
  155. fromView.layer.transform = CATransform3DIdentity
  156. toView.layer.transform = CATransform3DIdentity
  157. containerView.layer.transform = CATransform3DIdentity
  158. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  159. }
  160. }