ActivityIndicatorAnimationNewtonCradle.swift 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. //
  2. // ActivityIndicatorAnimationNewtonCradle.swift
  3. // IBAnimatable
  4. //
  5. // Created by phimage on 29/06/2018.
  6. // Copyright © 2018 IBAnimatable. All rights reserved.
  7. //
  8. import UIKit
  9. public class ActivityIndicatorAnimationNewtonCradle: ActivityIndicatorAnimating {
  10. // MARK: Properties
  11. fileprivate let duration: CFTimeInterval = 0.4
  12. fileprivate let ratio: CGFloat = 7
  13. fileprivate let ballCount: Int = 5
  14. fileprivate let timingFunction: TimingFunctionType = .easeOutQuad
  15. fileprivate var ballSize: CGFloat = 0
  16. // MARK: ActivityIndicatorAnimating
  17. public func configureAnimation(in layer: CALayer, size: CGSize, color: UIColor) {
  18. ballSize = size.height / ratio
  19. var xPos = (size.width - (CGFloat(ballCount) * ballSize)) / 2
  20. let yPos = size.height - ballSize / 2
  21. var balls: [CALayer] = []
  22. for _ in 0 ..< ballCount {
  23. let ball = ActivityIndicatorShape.circle.makeLayer(size: CGSize(width: ballSize, height: ballSize), color: color)
  24. ball.frame = CGRect(x: xPos, y: yPos, width: ballSize, height: ballSize)
  25. balls.append(ball)
  26. xPos += ballSize
  27. }
  28. let firstBall = balls.removeFirst()
  29. firstBall.add(makeAnimation(for: firstBall, in: layer, size: size, reverse: true), forKey: "animation")
  30. layer.addSublayer(firstBall)
  31. let lastBall = balls.removeLast()
  32. lastBall.add(makeAnimation(for: lastBall, in: layer, size: size, reverse: false), forKey: "animation")
  33. layer.addSublayer(lastBall)
  34. for ball in balls {
  35. layer.addSublayer(ball)
  36. }
  37. }
  38. }
  39. private extension ActivityIndicatorAnimationNewtonCradle {
  40. func makeAnimation(for ball: CALayer, in layer: CALayer, size: CGSize, reverse: Bool) -> CAAnimation {
  41. let rotateAnimation = CAKeyframeAnimation(keyPath: .position)
  42. rotateAnimation.path = UIBezierPath(arcCenter: CGPoint(x: ball.frame.origin.x + ballSize / 2, y: size.height / 2),
  43. radius: size.height / 2,
  44. startAngle: CGFloat.pi / 2,
  45. endAngle: reverse ? CGFloat.pi: 0,
  46. clockwise: reverse).cgPath
  47. rotateAnimation.duration = duration
  48. rotateAnimation.autoreverses = true
  49. rotateAnimation.timingFunctionType = timingFunction
  50. if reverse {
  51. rotateAnimation.beginTime = duration * 2
  52. }
  53. let animation = CAAnimationGroup()
  54. animation.animations = [rotateAnimation]
  55. animation.duration = duration * 4
  56. animation.repeatCount = .infinity
  57. animation.isRemovedOnCompletion = false
  58. return animation
  59. }
  60. }