ActivityIndicatorAnimationCircleDashStrokeSpin.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. //
  2. // ActivityIndicatorAnimationCircleDashStrokeSpin.swift
  3. // IBAnimatable
  4. //
  5. // Created by phimage on 03/05/2018.
  6. // Copyright © 2018 IBAnimatable. All rights reserved.
  7. //
  8. import UIKit
  9. public class ActivityIndicatorAnimationCircleDashStrokeSpin: ActivityIndicatorAnimating {
  10. // MARK: Properties
  11. let duration: Double = 1.5
  12. let rotationDuration: Double = 10
  13. let lineWidth: CGFloat = 4
  14. let instanceCount: Int = 12
  15. // MARK: ActivityIndicatorAnimating
  16. public func configureAnimation(in layer: CALayer, size: CGSize, color: UIColor) {
  17. let x = (layer.bounds.size.width - size.width) / 2
  18. let y = (layer.bounds.size.height - size.height) / 2
  19. let replicatorLayer = CAReplicatorLayer()
  20. replicatorLayer.frame = CGRect(x: x, y: y, width: size.width, height: size.height)
  21. let dotLayer = CAShapeLayer()
  22. dotLayer.lineCap = CAShapeLayerLineCap.round
  23. dotLayer.apply(mode: .stroke(lineWidth: lineWidth), color: color)
  24. let angle: CGFloat = 2 * .pi / CGFloat(instanceCount)
  25. replicatorLayer.instanceCount = instanceCount
  26. replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
  27. replicatorLayer.instanceDelay = 1.5 * duration / Double(instanceCount)
  28. let maxRadius = max(0, min(replicatorLayer.bounds.width, replicatorLayer.bounds.height)) / 2
  29. let radius: CGFloat = maxRadius - lineWidth / 2
  30. dotLayer.bounds = CGRect(x: 0, y: 0, width: 2 * maxRadius, height: 2 * maxRadius)
  31. dotLayer.position = CGPoint(x: replicatorLayer.bounds.width / 2, y: replicatorLayer.bounds.height / 2)
  32. let path = UIBezierPath(arcCenter: CGPoint(x: maxRadius, y: maxRadius),
  33. radius: radius,
  34. startAngle: -angle / 2 - CGFloat.pi / 2,
  35. endAngle: angle / 2 - CGFloat.pi / 2,
  36. clockwise: true)
  37. dotLayer.path = path.cgPath
  38. replicatorLayer.add(rotationAnimation, forKey: "rotate")
  39. dotLayer.add(strokeStartAnimation, forKey: "start")
  40. dotLayer.add(strokeEndAnimation, forKey: "end")
  41. dotLayer.add(lineWidthAnimation, forKey: "lineWidth")
  42. replicatorLayer.addSublayer(dotLayer)
  43. layer.addSublayer(replicatorLayer)
  44. }
  45. }
  46. // MARK: - Setup
  47. private extension ActivityIndicatorAnimationCircleDashStrokeSpin {
  48. var rotationAnimation: CABasicAnimation {
  49. let rotationAnimation = CABasicAnimation(keyPath: .rotation)
  50. rotationAnimation.byValue = Float.pi * 2
  51. rotationAnimation.timingFunctionType = .linear
  52. rotationAnimation.duration = rotationDuration
  53. return rotationAnimation
  54. }
  55. var strokeStartAnimation: CABasicAnimation {
  56. let strokeStartAnimation = CABasicAnimation(keyPath: .strokeStart)
  57. strokeStartAnimation.duration = duration
  58. strokeStartAnimation.timingFunctionType = .easeInOut
  59. strokeStartAnimation.fromValue = 0
  60. strokeStartAnimation.toValue = 0.5
  61. strokeStartAnimation.autoreverses = true
  62. strokeStartAnimation.repeatCount = .infinity
  63. return strokeStartAnimation
  64. }
  65. var strokeEndAnimation: CABasicAnimation {
  66. let strokeEndAnimation = CABasicAnimation(keyPath: .strokeEnd)
  67. strokeEndAnimation.duration = duration
  68. strokeEndAnimation.timingFunctionType = .easeInOut
  69. strokeEndAnimation.fromValue = 1.0
  70. strokeEndAnimation.toValue = 0.5
  71. strokeEndAnimation.autoreverses = true
  72. strokeEndAnimation.repeatCount = .infinity
  73. return strokeEndAnimation
  74. }
  75. var lineWidthAnimation: CABasicAnimation {
  76. let lineWidthAnimation = CABasicAnimation(keyPath: .lineWidth)
  77. lineWidthAnimation.fromValue = lineWidth
  78. lineWidthAnimation.toValue = 0.0
  79. lineWidthAnimation.duration = duration
  80. lineWidthAnimation.autoreverses = true
  81. lineWidthAnimation.repeatCount = .infinity
  82. lineWidthAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.55, 0.0, 0.45, 1.0)
  83. return lineWidthAnimation
  84. }
  85. }