|
| 1 | +import Foundation |
| 2 | +import UIKit |
| 3 | + |
| 4 | +extension UIColor { |
| 5 | + convenience init(_ hex6: UInt32) { |
| 6 | + let divisor = CGFloat(255) |
| 7 | + let red = CGFloat((hex6 & 0xFF0000) >> 16) / divisor |
| 8 | + let green = CGFloat((hex6 & 0x00FF00) >> 8) / divisor |
| 9 | + let blue = CGFloat((hex6 & 0x0000FF) >> 0) / divisor |
| 10 | + self.init(red: red, green: green, blue: blue, alpha: 1.0) |
| 11 | + } |
| 12 | +} |
| 13 | + |
| 14 | +class TKProgressView: UIView { |
| 15 | + var colors = [ |
| 16 | + UIColor(0xee1c27),UIColor(0xbc0271),UIColor(0x612d92),UIColor(0x283897), |
| 17 | + UIColor(0x016db8),UIColor(0x02a2b8),UIColor(0x00a666),UIColor(0xa7d04e), |
| 18 | + UIColor(0xfef200),UIColor(0xfeaf16),UIColor(0xf58020),UIColor(0xf35724)] |
| 19 | + |
| 20 | + fileprivate var startIndex = 3 |
| 21 | + |
| 22 | + var isCircular = true |
| 23 | + var numberOfBalls = 12 |
| 24 | + var speed: Double = 0.75 |
| 25 | + |
| 26 | + fileprivate var timeStep: Int = 0 |
| 27 | + fileprivate var radius: CGFloat = 0 |
| 28 | + fileprivate var displayLink: CADisplayLink? |
| 29 | + |
| 30 | + required override init(frame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100)) { |
| 31 | + super.init(frame: frame) |
| 32 | + |
| 33 | + radius = min(frame.width, frame.height) / 3 |
| 34 | + } |
| 35 | + required init?(coder aDecoder: NSCoder) { |
| 36 | + super.init(coder: aDecoder) |
| 37 | + |
| 38 | + frame.size.width = 100 |
| 39 | + frame.size.height = 100 |
| 40 | + |
| 41 | + radius = min(frame.width, frame.height) / 3 |
| 42 | + } |
| 43 | + |
| 44 | + func startAnimating() { |
| 45 | + displayLink = UIScreen.main.displayLink(withTarget: self, selector: #selector(TKProgressView.animateBalls)) |
| 46 | + displayLink?.add(to: .current, forMode: .defaultRunLoopMode) |
| 47 | + } |
| 48 | + |
| 49 | + func stopAnimating() { |
| 50 | + displayLink?.invalidate() |
| 51 | + timeStep = 0 |
| 52 | + } |
| 53 | + |
| 54 | + func clear() { |
| 55 | + layer.sublayers?.removeAll() |
| 56 | + } |
| 57 | + |
| 58 | + func drawBall(_ x: CGFloat, _ y: CGFloat, _ color: UIColor) { |
| 59 | + let dot = CAShapeLayer() |
| 60 | + dot.path = UIBezierPath(ovalIn: CGRect(x: x, y: y, width: 7, height: 7)).cgPath |
| 61 | + dot.fillColor = color.cgColor |
| 62 | + dot.strokeColor = UIColor.clear.cgColor |
| 63 | + dot.lineWidth = 0 |
| 64 | + layer.addSublayer(dot) |
| 65 | + } |
| 66 | + |
| 67 | + func animateBalls() { |
| 68 | + clear() |
| 69 | + |
| 70 | + for i in startIndex ..< numberOfBalls + startIndex { |
| 71 | + drawBall(getX(i: i, timeStep: timeStep), getY(i: i, timeStep: timeStep), colors[i-startIndex]) |
| 72 | + } |
| 73 | + |
| 74 | + timeStep += 1 |
| 75 | + } |
| 76 | + |
| 77 | + func getR(i: Int) -> CGFloat { |
| 78 | + return CGFloat((CGFloat(1+i) / CGFloat(numberOfBalls)) * radius) |
| 79 | + } |
| 80 | + |
| 81 | + func getT(i: Int, timeStep: Int) -> Double { |
| 82 | + return Double(timeStep) * (Double(i) / 100 * speed + 0.005) |
| 83 | + } |
| 84 | + |
| 85 | + func getX(i: Int, timeStep: Int) -> CGFloat { |
| 86 | + return (frame.width / 2) + CGFloat(getR(i: i)) * cos(CGFloat(getT(i: i, timeStep: timeStep))) |
| 87 | + } |
| 88 | + |
| 89 | + func getY(i: Int, timeStep: Int) -> CGFloat { |
| 90 | + if isCircular { |
| 91 | + return (frame.height / 2) + CGFloat(getR(i: i)) * sin(CGFloat(getT(i: i, timeStep: timeStep))) |
| 92 | + } else { |
| 93 | + return frame.height / 2 |
| 94 | + } |
| 95 | + } |
| 96 | +} |
0 commit comments