PresentrAnimation.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. //
  2. // PresentrAnimation.swift
  3. // Presentr
  4. //
  5. // Created by Daniel Lozano on 5/14/16.
  6. // Copyright © 2016 danielozano. All rights reserved.
  7. //
  8. import UIKit
  9. /// Simplified wrapper for the UIViewControllerContextTransitioning protocol.
  10. public struct PresentrTransitionContext {
  11. public let containerView: UIView
  12. public let initialFrame: CGRect
  13. public let finalFrame: CGRect
  14. public let isPresenting: Bool
  15. public let fromViewController: UIViewController?
  16. public let toViewController: UIViewController?
  17. public let fromView: UIView?
  18. public let toView: UIView?
  19. public let animatingViewController: UIViewController?
  20. public let animatingView: UIView?
  21. }
  22. /// Options for the UIView animation.
  23. public enum AnimationOptions {
  24. case normal(duration: TimeInterval)
  25. case spring(duration: TimeInterval, delay: TimeInterval, damping: CGFloat, velocity: CGFloat)
  26. }
  27. /// Class that handles animating the transition. Override this class if you want to create your own transition animation.
  28. open class PresentrAnimation: NSObject {
  29. public var options: AnimationOptions
  30. public init(options: AnimationOptions = .normal(duration: 0.4)) {
  31. self.options = options
  32. }
  33. /// For simple transitions, override this method to calculate an initial frame for the animation. For more complex animations override beforeAnimation & performAnimation. Only override this method OR beforeAnimation & performAnimation. This method won't even be called if you override beforeAnimation.
  34. ///
  35. /// - Parameters:
  36. /// - containerFrame: The container frame for the animation.
  37. /// - finalFrame: The final frame for the animation.
  38. /// - Returns: The initial frame.
  39. open func transform(containerFrame: CGRect, finalFrame: CGRect) -> CGRect {
  40. var initialFrame = finalFrame
  41. initialFrame.origin.y = containerFrame.height + initialFrame.height
  42. return initialFrame
  43. }
  44. /// Actions to be performed in preparation, before an animation.
  45. ///
  46. /// - Parameter transitionContext: The context with everything needed for the animiation.
  47. open func beforeAnimation(using transitionContext: PresentrTransitionContext) {
  48. let finalFrameForVC = transitionContext.finalFrame
  49. let initialFrameForVC = transform(containerFrame: transitionContext.containerView.frame, finalFrame: finalFrameForVC)
  50. let initialFrame = transitionContext.isPresenting ? initialFrameForVC : finalFrameForVC
  51. transitionContext.animatingView?.frame = initialFrame
  52. }
  53. /// Actions to be performed for the animation.
  54. ///
  55. /// - Parameter transitionContext: The context with everything needed for the animiation.
  56. open func performAnimation(using transitionContext: PresentrTransitionContext) {
  57. let finalFrameForVC = transitionContext.finalFrame
  58. let initialFrameForVC = transform(containerFrame: transitionContext.containerView.frame, finalFrame: finalFrameForVC)
  59. let finalFrame = transitionContext.isPresenting ? finalFrameForVC : initialFrameForVC
  60. transitionContext.animatingView?.frame = finalFrame
  61. }
  62. /// Actions to be performed after the animation.
  63. ///
  64. /// - Parameter transitionContext: The context with everything needed for the animiation.
  65. open func afterAnimation(using transitionContext: PresentrTransitionContext) {
  66. // Any cleanup to be done after the animation is over.
  67. }
  68. }
  69. // MARK: - UIViewControllerAnimatedTransitioning
  70. extension PresentrAnimation: UIViewControllerAnimatedTransitioning {
  71. public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
  72. switch options {
  73. case let .normal(duration):
  74. return duration
  75. case let .spring(duration, _, _, _):
  76. return duration
  77. }
  78. }
  79. public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  80. let containerView = transitionContext.containerView
  81. let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
  82. let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
  83. let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
  84. let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
  85. let isPresenting: Bool = (toViewController?.presentingViewController == fromViewController)
  86. let animatingVC = isPresenting ? toViewController : fromViewController
  87. let animatingView = isPresenting ? toView : fromView
  88. let initialFrame = transitionContext.initialFrame(for: animatingVC!)
  89. let finalFrame = transitionContext.finalFrame(for: animatingVC!)
  90. let presentrContext = PresentrTransitionContext(containerView: containerView,
  91. initialFrame: initialFrame,
  92. finalFrame: finalFrame,
  93. isPresenting: isPresenting,
  94. fromViewController: fromViewController,
  95. toViewController: toViewController,
  96. fromView: fromView,
  97. toView: toView,
  98. animatingViewController: animatingVC,
  99. animatingView: animatingView)
  100. if isPresenting {
  101. containerView.addSubview(toView!)
  102. }
  103. switch options {
  104. case let .normal(duration):
  105. animate(presentrContext: presentrContext,
  106. transitionContext: transitionContext,
  107. duration: duration)
  108. case let .spring(duration, delay, damping, velocity):
  109. animateWithSpring(presentrContext: presentrContext,
  110. transitionContext: transitionContext,
  111. duration: duration,
  112. delay: delay,
  113. damping: damping,
  114. velocity: velocity)
  115. }
  116. }
  117. private func animate(presentrContext: PresentrTransitionContext, transitionContext: UIViewControllerContextTransitioning, duration: TimeInterval) {
  118. beforeAnimation(using: presentrContext)
  119. UIView.animate(withDuration: duration, animations: {
  120. self.performAnimation(using: presentrContext)
  121. }) { (completed) in
  122. self.afterAnimation(using: presentrContext)
  123. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  124. }
  125. }
  126. private func animateWithSpring(presentrContext: PresentrTransitionContext, transitionContext: UIViewControllerContextTransitioning, duration: TimeInterval, delay: TimeInterval, damping: CGFloat, velocity: CGFloat) {
  127. beforeAnimation(using: presentrContext)
  128. UIView.animate(withDuration: duration,
  129. delay: delay,
  130. usingSpringWithDamping: damping,
  131. initialSpringVelocity: velocity,
  132. options: [],
  133. animations: {
  134. self.performAnimation(using: presentrContext)
  135. }) { (completed) in
  136. self.afterAnimation(using: presentrContext)
  137. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  138. }
  139. }
  140. }