|
|
|
@@ -37,7 +37,29 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static func Mod(value: Int, range: Int, minMax: (Int, Int)) -> Int {
|
|
|
|
|
static func InverseLerp(value: CGFloat, minMax: (CGFloat, CGFloat)) -> CGFloat {
|
|
|
|
|
return (value - minMax.0) / (minMax.1 - minMax.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static func Lerp(value: CGFloat, minMax: (CGFloat, CGFloat)) -> CGFloat {
|
|
|
|
|
return (minMax.1 - minMax.0) * value + minMax.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static func ColorLerp(value: CGFloat, minMax: (UIColor, UIColor)) -> UIColor {
|
|
|
|
|
let clampedValue = Clamp(value, minMax: (0, 1))
|
|
|
|
|
|
|
|
|
|
let zero: CGFloat = 0
|
|
|
|
|
|
|
|
|
|
var (r0, g0, b0, a0) = (zero, zero, zero, zero)
|
|
|
|
|
minMax.0.getRed(&r0, green: &g0, blue: &b0, alpha: &a0)
|
|
|
|
|
|
|
|
|
|
var (r1, g1, b1, a1) = (zero, zero, zero, zero)
|
|
|
|
|
minMax.1.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
|
|
|
|
|
|
|
|
|
|
return UIColor(red: Lerp(clampedValue, minMax: (r0, r1)), green: Lerp(clampedValue, minMax: (g0, g1)), blue: Lerp(clampedValue, minMax: (b0, b1)), alpha: Lerp(clampedValue, minMax: (a0, a1)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static func Mod(value: Double, range: Double, minMax: (Double, Double)) -> Double {
|
|
|
|
|
let (min, max) = minMax
|
|
|
|
|
assert(abs(range) <= abs(max - min), "range should be <= than the interval")
|
|
|
|
|
if value >= min && value <= max {
|
|
|
|
@@ -62,7 +84,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@IBInspectable public var angle: Int = 0 {
|
|
|
|
|
@IBInspectable public var angle: Double = 0 {
|
|
|
|
|
didSet {
|
|
|
|
|
if self.isAnimating() {
|
|
|
|
|
self.pauseAnimation()
|
|
|
|
@@ -71,7 +93,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@IBInspectable public var startAngle: Int = 0 {
|
|
|
|
|
@IBInspectable public var startAngle: Double = 0 {
|
|
|
|
|
didSet {
|
|
|
|
|
progressLayer.startAngle = UtilityFunctions.Mod(startAngle, range: 360, minMax: (0,360))
|
|
|
|
|
progressLayer.setNeedsDisplay()
|
|
|
|
@@ -91,6 +113,12 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@IBInspectable public var lerpColorMode: Bool = false {
|
|
|
|
|
didSet {
|
|
|
|
|
progressLayer.lerpColorMode = lerpColorMode
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@IBInspectable public var gradientRotateSpeed: CGFloat = 0 {
|
|
|
|
|
didSet {
|
|
|
|
|
progressLayer.gradientRotateSpeed = gradientRotateSpeed
|
|
|
|
@@ -204,6 +232,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
progressLayer.startAngle = UtilityFunctions.Mod(startAngle, range: 360, minMax: (0,360))
|
|
|
|
|
progressLayer.clockwise = clockwise
|
|
|
|
|
progressLayer.roundedCorners = roundedCorners
|
|
|
|
|
progressLayer.lerpColorMode = lerpColorMode
|
|
|
|
|
progressLayer.gradientRotateSpeed = gradientRotateSpeed
|
|
|
|
|
progressLayer.glowAmount = UtilityFunctions.Clamp(glowAmount, minMax: (0, 1))
|
|
|
|
|
progressLayer.glowMode = glowMode
|
|
|
|
@@ -228,7 +257,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
progressLayer.setNeedsDisplay()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func animateFromAngle(fromAngle: Int, toAngle: Int, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
|
|
|
|
public func animateFromAngle(fromAngle: Double, toAngle: Double, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
|
|
|
|
if isAnimating() {
|
|
|
|
|
pauseAnimation()
|
|
|
|
|
}
|
|
|
|
@@ -253,7 +282,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
progressLayer.addAnimation(animation, forKey: "angle")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func animateToAngle(toAngle: Int, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
|
|
|
|
public func animateToAngle(toAngle: Double, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
|
|
|
|
if isAnimating() {
|
|
|
|
|
pauseAnimation()
|
|
|
|
|
}
|
|
|
|
@@ -304,13 +333,13 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class KDCircularProgressViewLayer: CALayer {
|
|
|
|
|
@NSManaged var angle: Int
|
|
|
|
|
@NSManaged var angle: Double
|
|
|
|
|
var radius: CGFloat! {
|
|
|
|
|
didSet {
|
|
|
|
|
invalidateGradientCache()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var startAngle: Int!
|
|
|
|
|
var startAngle: Double!
|
|
|
|
|
var clockwise: Bool! {
|
|
|
|
|
didSet {
|
|
|
|
|
if clockwise != oldValue {
|
|
|
|
@@ -319,6 +348,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var roundedCorners: Bool!
|
|
|
|
|
var lerpColorMode: Bool!
|
|
|
|
|
var gradientRotateSpeed: CGFloat! {
|
|
|
|
|
didSet {
|
|
|
|
|
invalidateGradientCache()
|
|
|
|
@@ -340,7 +370,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
|
|
|
|
|
private struct GlowConstants {
|
|
|
|
|
private static let sizeToGlowRatio: CGFloat = 0.00015
|
|
|
|
|
static func glowAmountForAngle(angle: Int, glowAmount: CGFloat, glowMode: KDCircularProgressGlowMode, size: CGFloat) -> CGFloat {
|
|
|
|
|
static func glowAmountForAngle(angle: Double, glowAmount: CGFloat, glowMode: KDCircularProgressGlowMode, size: CGFloat) -> CGFloat {
|
|
|
|
|
switch glowMode {
|
|
|
|
|
case .Forward:
|
|
|
|
|
return CGFloat(angle) * size * sizeToGlowRatio * glowAmount
|
|
|
|
@@ -366,6 +396,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
startAngle = progressLayer.startAngle
|
|
|
|
|
clockwise = progressLayer.clockwise
|
|
|
|
|
roundedCorners = progressLayer.roundedCorners
|
|
|
|
|
lerpColorMode = progressLayer.lerpColorMode
|
|
|
|
|
gradientRotateSpeed = progressLayer.gradientRotateSpeed
|
|
|
|
|
glowAmount = progressLayer.glowAmount
|
|
|
|
|
glowMode = progressLayer.glowMode
|
|
|
|
@@ -421,7 +452,7 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
CGContextClipToMask(ctx, bounds, drawMask)
|
|
|
|
|
|
|
|
|
|
//Gradient - Fill
|
|
|
|
|
if colorsArray.count > 1 {
|
|
|
|
|
if !lerpColorMode && colorsArray.count > 1 {
|
|
|
|
|
var componentsArray: [CGFloat] = []
|
|
|
|
|
let rgbColorsArray: [UIColor] = colorsArray.map {c in // Make sure every color in colors array is in RGB color space
|
|
|
|
|
if CGColorGetNumberOfComponents(c.CGColor) == 2 {
|
|
|
|
@@ -439,11 +470,30 @@ public class KDCircularProgress: UIView {
|
|
|
|
|
|
|
|
|
|
drawGradientWithContext(ctx, componentsArray: componentsArray)
|
|
|
|
|
} else {
|
|
|
|
|
if colorsArray.count == 1 {
|
|
|
|
|
fillRectWithContext(ctx, color: colorsArray[0])
|
|
|
|
|
|
|
|
|
|
var color: UIColor! = nil
|
|
|
|
|
if colorsArray.count == 0 {
|
|
|
|
|
color = UIColor.whiteColor()
|
|
|
|
|
} else if colorsArray.count == 1 {
|
|
|
|
|
color = colorsArray[0]
|
|
|
|
|
} else {
|
|
|
|
|
fillRectWithContext(ctx, color: UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0))
|
|
|
|
|
|
|
|
|
|
// lerpColorMode is true
|
|
|
|
|
|
|
|
|
|
let t = CGFloat(reducedAngle) / 360
|
|
|
|
|
let steps = colorsArray.count - 1;
|
|
|
|
|
let step = 1 / CGFloat(steps);
|
|
|
|
|
for i in 1...steps {
|
|
|
|
|
let fi = CGFloat(i)
|
|
|
|
|
if (t <= fi * step || i == steps) {
|
|
|
|
|
let colorT = UtilityFunctions.InverseLerp(t, minMax: ((fi - 1) * step, fi * step))
|
|
|
|
|
color = UtilityFunctions.ColorLerp(colorT, minMax: (colorsArray[i - 1], colorsArray[i]));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fillRectWithContext(ctx, color: color)
|
|
|
|
|
}
|
|
|
|
|
CGContextRestoreGState(ctx)
|
|
|
|
|
UIGraphicsPopContext()
|
|
|
|
|