ActivityIndicatorAnimationBallTrianglePath.swift 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. //
  2. // Created by Tom Baranes on 23/08/16.
  3. // Copyright (c) 2016 IBAnimatable. All rights reserved.
  4. //
  5. import UIKit
  6. public class ActivityIndicatorAnimationBallTrianglePath: ActivityIndicatorAnimating {
  7. // MARK: Properties
  8. fileprivate let duration: CFTimeInterval = 2
  9. fileprivate let timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
  10. // MARK: ActivityIndicatorAnimating
  11. public func configureAnimation(in layer: CALayer, size: CGSize, color: UIColor) {
  12. let circleSize = size.width / 5
  13. let deltaX = size.width / 2 - circleSize / 2
  14. let deltaY = size.height / 2 - circleSize / 2
  15. let x = (layer.bounds.size.width - size.width) / 2
  16. let y = (layer.bounds.size.height - size.height) / 2
  17. let animation = defaultAnimation
  18. let topCenterCircle = ActivityIndicatorShape.ring.makeLayer(size: CGSize(width: circleSize, height: circleSize), color: color)
  19. change(animation: animation, values: ["{0,0}", "{hx,fy}", "{-hx,fy}", "{0,0}"], deltaX: deltaX, deltaY: deltaY)
  20. topCenterCircle.frame = CGRect(x: x + size.width / 2 - circleSize / 2, y: y, width: circleSize, height: circleSize)
  21. topCenterCircle.add(animation, forKey: "animation")
  22. layer.addSublayer(topCenterCircle)
  23. let bottomLeftCircle = ActivityIndicatorShape.ring.makeLayer(size: CGSize(width: circleSize, height: circleSize), color: color)
  24. change(animation: animation, values: ["{0,0}", "{hx,-fy}", "{fx,0}", "{0,0}"], deltaX: deltaX, deltaY: deltaY)
  25. bottomLeftCircle.frame = CGRect(x: x, y: y + size.height - circleSize, width: circleSize, height: circleSize)
  26. bottomLeftCircle.add(animation, forKey: "animation")
  27. layer.addSublayer(bottomLeftCircle)
  28. let bottomRightCircle = ActivityIndicatorShape.ring.makeLayer(size: CGSize(width: circleSize, height: circleSize), color: color)
  29. change(animation: animation, values: ["{0,0}", "{-fx,0}", "{-hx,-fy}", "{0,0}"], deltaX: deltaX, deltaY: deltaY)
  30. bottomRightCircle.frame = CGRect(x: x + size.width - circleSize, y: y + size.height - circleSize, width: circleSize, height: circleSize)
  31. bottomRightCircle.add(animation, forKey: "animation")
  32. layer.addSublayer(bottomRightCircle)
  33. }
  34. }
  35. // MARK: - Setup
  36. private extension ActivityIndicatorAnimationBallTrianglePath {
  37. var defaultAnimation: CAKeyframeAnimation {
  38. let animation = CAKeyframeAnimation(keyPath: .transform)
  39. animation.keyTimes = [0, 0.33, 0.66, 1]
  40. animation.timingFunctions = [timingFunction, timingFunction, timingFunction]
  41. animation.duration = duration
  42. animation.repeatCount = .infinity
  43. animation.isRemovedOnCompletion = false
  44. return animation
  45. }
  46. func change(animation: CAKeyframeAnimation, values rawValues: [String], deltaX: CGFloat, deltaY: CGFloat) {
  47. let values = NSMutableArray(capacity: 5)
  48. for rawValue in rawValues {
  49. let point = NSCoder.cgPoint(for: translateString(valueString: rawValue, deltaX: deltaX, deltaY: deltaY))
  50. values.add(NSValue(caTransform3D: CATransform3DMakeTranslation(point.x, point.y, 0)))
  51. }
  52. animation.values = values as [AnyObject]
  53. }
  54. func translateString(valueString: String, deltaX: CGFloat, deltaY: CGFloat) -> String {
  55. let valueMutableString = NSMutableString(string: valueString)
  56. let fullDeltaX = 2 * deltaX
  57. let fullDeltaY = 2 * deltaY
  58. var range = NSRange(location: 0, length: valueMutableString.length)
  59. valueMutableString.replaceOccurrences(of: "hx", with: "\(deltaX)", options: .caseInsensitive, range: range)
  60. range.length = valueMutableString.length
  61. valueMutableString.replaceOccurrences(of: "fx", with: "\(fullDeltaX)", options: .caseInsensitive, range: range)
  62. range.length = valueMutableString.length
  63. valueMutableString.replaceOccurrences(of: "hy", with: "\(deltaY)", options: .caseInsensitive, range: range)
  64. range.length = valueMutableString.length
  65. valueMutableString.replaceOccurrences(of: "fy", with: "\(fullDeltaY)", options: .caseInsensitive, range: range)
  66. return valueMutableString as String
  67. }
  68. }