ExplodeAnimator.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. //
  2. // Created by Tom Baranes on 03/04/16.
  3. // Copyright © 2016 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. public class ExplodeAnimator: 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 xFactor: CGFloat
  14. fileprivate var minAngle: CGFloat
  15. fileprivate var maxAngle: CGFloat
  16. public init(xFactor: CGFloat?, minAngle: CGFloat?, maxAngle: CGFloat?, duration: Duration) {
  17. transitionDuration = duration
  18. self.xFactor = xFactor ?? 10.0
  19. self.minAngle = minAngle ?? -10.0
  20. self.maxAngle = maxAngle ?? 10.0
  21. transitionAnimationType = .explode(xFactor: self.xFactor, minAngle: self.minAngle, maxAngle: self.maxAngle)
  22. reverseAnimationType = .explode(xFactor: self.xFactor, minAngle: self.minAngle, maxAngle: self.maxAngle)
  23. super.init()
  24. }
  25. }
  26. extension ExplodeAnimator: UIViewControllerAnimatedTransitioning {
  27. public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
  28. return retrieveTransitionDuration(transitionContext: transitionContext)
  29. }
  30. public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  31. let (tempfromView, tempToView, tempContainerView) = retrieveViews(transitionContext: transitionContext)
  32. guard let fromView = tempfromView, let toView = tempToView, let containerView = tempContainerView else {
  33. transitionContext.completeTransition(true)
  34. return
  35. }
  36. containerView.insertSubview(toView, at: 0)
  37. let snapshots = makeSnapshots(toView: toView, fromView: fromView, containerView: containerView)
  38. containerView.sendSubviewToBack(fromView)
  39. animateSnapshotsExplode(snapshots) {
  40. if transitionContext.transitionWasCancelled {
  41. containerView.bringSubviewToFront(fromView)
  42. }
  43. transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  44. }
  45. }
  46. }
  47. private extension ExplodeAnimator {
  48. func makeSnapshots(toView: UIView, fromView: UIView, containerView: UIView) -> [UIView] {
  49. let size = toView.frame.size
  50. var snapshots = [UIView]()
  51. let yFactor = xFactor * size.height / size.width
  52. let fromViewSnapshot = fromView.snapshotView(afterScreenUpdates: false)
  53. for x in stride(from: 0.0, to: Double(size.width), by: Double(size.width / xFactor)) {
  54. for y in stride(from: 0.0, to: Double(size.height), by: Double(size.width / yFactor)) {
  55. let snapshotRegion = CGRect(x: CGFloat(x), y: CGFloat(y), width: size.width / xFactor, height: size.height / yFactor)
  56. let snapshot = fromViewSnapshot?.resizableSnapshotView(from: snapshotRegion, afterScreenUpdates: false, withCapInsets: .zero)
  57. snapshot?.frame = snapshotRegion
  58. containerView.addSubview(snapshot!)
  59. snapshots.append(snapshot!)
  60. }
  61. }
  62. return snapshots
  63. }
  64. func animateSnapshotsExplode(_ snapshots: [UIView], completion: @escaping AnimatableCompletion) {
  65. UIView.animate(withDuration: transitionDuration, animations: {
  66. snapshots.forEach {
  67. let xOffset = self.randomFloat(from: -100.0, to: 100.0)
  68. let yOffset = self.randomFloat(from: -100.0, to: 100.0)
  69. let angle = self.randomFloat(from: self.minAngle, to: self.maxAngle)
  70. let translateTransform = CGAffineTransform(translationX: $0.frame.origin.x - xOffset, y: $0.frame.origin.y - yOffset)
  71. let angleTransform = translateTransform.rotated(by: angle)
  72. let scaleTransform = angleTransform.scaledBy(x: 0.01, y: 0.01)
  73. $0.transform = scaleTransform
  74. $0.alpha = 0.0
  75. }
  76. },
  77. completion: { _ in
  78. snapshots.forEach { $0.removeFromSuperview() }
  79. completion()
  80. })
  81. }
  82. func randomFloat(from lower: CGFloat, to upper: CGFloat) -> CGFloat {
  83. return CGFloat(arc4random_uniform(UInt32(upper - lower))) + lower
  84. }
  85. }