Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb5f616d82 | |||
| aa4edacabf | |||
| 6475bc0a58 |
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'KDCircularProgress'
|
||||
s.version = '1.5.0'
|
||||
s.version = '1.4.1'
|
||||
s.license = 'MIT'
|
||||
s.summary = 'A circular progress view with gradients written in Swift'
|
||||
s.homepage = 'https://github.com/kaandedeoglu/KDCircularProgress'
|
||||
|
||||
@@ -9,15 +9,19 @@
|
||||
import UIKit
|
||||
|
||||
public enum KDCircularProgressGlowMode {
|
||||
case forward, reverse, constant, noGlow
|
||||
case Forward, Reverse, Constant, NoGlow
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
public class KDCircularProgress: UIView,CAAnimationDelegate {
|
||||
|
||||
private struct Conversion {
|
||||
static func degreesToRadians (value:CGFloat) -> CGFloat {
|
||||
return value * CGFloat.pi / 180.0
|
||||
return value * CGFloat(M_PI) / 180.0
|
||||
}
|
||||
|
||||
static func radiansToDegrees (value:CGFloat) -> CGFloat {
|
||||
return value * 180.0 / CGFloat(M_PI)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +46,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
|
||||
static func colorLerp(value: CGFloat, minMax: (UIColor, UIColor)) -> UIColor {
|
||||
let clampedValue = clamp(value: value, minMax: (0, 1))
|
||||
let clampedValue = clamp(value, minMax: (0, 1))
|
||||
|
||||
let zero: CGFloat = 0
|
||||
|
||||
@@ -52,7 +56,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
var (r1, g1, b1, a1) = (zero, zero, zero, zero)
|
||||
minMax.1.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
|
||||
|
||||
return UIColor(red: lerp(value: clampedValue, minMax: (r0, r1)), green: lerp(value: clampedValue, minMax: (g0, g1)), blue: lerp(value: clampedValue, minMax: (b0, b1)), alpha: lerp(value: clampedValue, minMax: (a0, 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 {
|
||||
@@ -61,9 +65,9 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
if value >= min && value <= max {
|
||||
return value
|
||||
} else if value < min {
|
||||
return mod(value: value + range, range: range, minMax: minMax)
|
||||
return mod(value + range, range: range, minMax: minMax)
|
||||
} else {
|
||||
return mod(value: value - range, range: range, minMax: minMax)
|
||||
return mod(value - range, range: range, minMax: minMax)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +95,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
@IBInspectable public var startAngle: Double = 0 {
|
||||
didSet {
|
||||
startAngle = Utility.mod(value: startAngle, range: 360, minMax: (0, 360))
|
||||
startAngle = Utility.mod(startAngle, range: 360, minMax: (0, 360))
|
||||
progressLayer.startAngle = startAngle
|
||||
progressLayer.setNeedsDisplay()
|
||||
}
|
||||
@@ -124,12 +128,12 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
@IBInspectable public var glowAmount: CGFloat = 1.0 {//Between 0 and 1
|
||||
didSet {
|
||||
glowAmount = Utility.clamp(value: glowAmount, minMax: (0, 1))
|
||||
glowAmount = Utility.clamp(glowAmount, minMax: (0, 1))
|
||||
progressLayer.glowAmount = glowAmount
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable public var glowMode: KDCircularProgressGlowMode = .forward {
|
||||
@IBInspectable public var glowMode: KDCircularProgressGlowMode = .Forward {
|
||||
didSet {
|
||||
progressLayer.glowMode = glowMode
|
||||
}
|
||||
@@ -137,19 +141,19 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
@IBInspectable public var progressThickness: CGFloat = 0.4 {//Between 0 and 1
|
||||
didSet {
|
||||
progressThickness = Utility.clamp(value: progressThickness, minMax: (0, 1))
|
||||
progressThickness = Utility.clamp(progressThickness, minMax: (0, 1))
|
||||
progressLayer.progressThickness = progressThickness/2
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable public var trackThickness: CGFloat = 0.5 {//Between 0 and 1
|
||||
didSet {
|
||||
trackThickness = Utility.clamp(value: trackThickness, minMax: (0, 1))
|
||||
trackThickness = Utility.clamp(trackThickness, minMax: (0, 1))
|
||||
progressLayer.trackThickness = trackThickness/2
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable public var trackColor: UIColor = .black {
|
||||
@IBInspectable public var trackColor: UIColor = .blackColor() {
|
||||
didSet {
|
||||
progressLayer.trackColor = trackColor
|
||||
progressLayer.setNeedsDisplay()
|
||||
@@ -161,7 +165,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
if let color = progressInsideFillColor {
|
||||
progressLayer.progressInsideFillColor = color
|
||||
} else {
|
||||
progressLayer.progressInsideFillColor = .clear
|
||||
progressLayer.progressInsideFillColor = .clearColor()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +176,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
|
||||
set(newValue) {
|
||||
setColors(colors: newValue)
|
||||
setColors(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +190,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
isUserInteractionEnabled = false
|
||||
userInteractionEnabled = false
|
||||
setInitialValues()
|
||||
refreshValues()
|
||||
checkAndSetIBColors()
|
||||
@@ -194,13 +198,13 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
convenience public init(frame:CGRect, colors: UIColor...) {
|
||||
self.init(frame: frame)
|
||||
setColors(colors: colors)
|
||||
setColors(colors)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)!
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
isUserInteractionEnabled = false
|
||||
userInteractionEnabled = false
|
||||
setInitialValues()
|
||||
refreshValues()
|
||||
}
|
||||
@@ -209,7 +213,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
checkAndSetIBColors()
|
||||
}
|
||||
|
||||
override public class var layerClass: AnyClass {
|
||||
override public class func layerClass() -> AnyClass {
|
||||
return KDCircularProgressViewLayer.self
|
||||
}
|
||||
|
||||
@@ -220,8 +224,8 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
|
||||
private func setInitialValues() {
|
||||
radius = (frame.size.width/2.0) * 0.8 //We always apply a 20% padding, stopping glows from being clipped
|
||||
backgroundColor = .clear
|
||||
setColors(colors: .white, .cyan)
|
||||
backgroundColor = .clearColor()
|
||||
setColors(.whiteColor(), .cyanColor())
|
||||
}
|
||||
|
||||
private func refreshValues() {
|
||||
@@ -241,12 +245,12 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
private func checkAndSetIBColors() {
|
||||
let nonNilColors = [IBColor1, IBColor2, IBColor3].flatMap { $0 }
|
||||
if !nonNilColors.isEmpty {
|
||||
setColors(colors: nonNilColors)
|
||||
setColors(nonNilColors)
|
||||
}
|
||||
}
|
||||
|
||||
public func setColors(colors: UIColor...) {
|
||||
setColors(colors: colors)
|
||||
setColors(colors)
|
||||
}
|
||||
|
||||
private func setColors(colors: [UIColor]) {
|
||||
@@ -254,17 +258,17 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
progressLayer.setNeedsDisplay()
|
||||
}
|
||||
|
||||
public func animate(fromAngle: Double, toAngle: Double, duration: TimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
||||
public func animateFromAngle(fromAngle: Double, toAngle: Double, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
||||
if isAnimating() {
|
||||
pauseAnimation()
|
||||
}
|
||||
|
||||
let animationDuration: TimeInterval
|
||||
let animationDuration: NSTimeInterval
|
||||
if relativeDuration {
|
||||
animationDuration = duration
|
||||
} else {
|
||||
let traveledAngle = Utility.mod(value: toAngle - fromAngle, range: 360, minMax: (0, 360))
|
||||
let scaledDuration = (TimeInterval(traveledAngle) * duration) / 360
|
||||
let traveledAngle = Utility.mod(toAngle - fromAngle, range: 360, minMax: (0, 360))
|
||||
let scaledDuration = (NSTimeInterval(traveledAngle) * duration) / 360
|
||||
animationDuration = scaledDuration
|
||||
}
|
||||
|
||||
@@ -276,19 +280,18 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
angle = toAngle
|
||||
animationCompletionBlock = completion
|
||||
|
||||
progressLayer.add(animation, forKey: "angle")
|
||||
progressLayer.addAnimation(animation, forKey: "angle")
|
||||
}
|
||||
|
||||
public func animate(toAngle: Double, duration: TimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
||||
public func animateToAngle(toAngle: Double, duration: NSTimeInterval, relativeDuration: Bool = true, completion: ((Bool) -> Void)?) {
|
||||
if isAnimating() {
|
||||
pauseAnimation()
|
||||
}
|
||||
animate(fromAngle: angle, toAngle: toAngle, duration: duration, relativeDuration: relativeDuration, completion: completion)
|
||||
animateFromAngle(angle, toAngle: toAngle, duration: duration, relativeDuration: relativeDuration, completion: completion)
|
||||
}
|
||||
|
||||
public func pauseAnimation() {
|
||||
guard let presentationLayer = progressLayer.presentation() else { return }
|
||||
|
||||
guard let presentationLayer = progressLayer.presentationLayer() else { return }
|
||||
let currentValue = presentationLayer.angle
|
||||
progressLayer.removeAllAnimations()
|
||||
animationCompletionBlock = nil
|
||||
@@ -302,13 +305,13 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
|
||||
public func isAnimating() -> Bool {
|
||||
return progressLayer.animation(forKey: "angle") != nil
|
||||
return progressLayer.animationForKey("angle") != nil
|
||||
}
|
||||
|
||||
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
||||
public func animationDidStop(anim: CAAnimation, finished flag: Bool) {
|
||||
if let completionBlock = animationCompletionBlock {
|
||||
if flag {
|
||||
animationCompletionBlock = nil
|
||||
animationCompletionBlock = nil
|
||||
}
|
||||
|
||||
completionBlock(flag)
|
||||
@@ -321,7 +324,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public override func willMove(toSuperview newSuperview: UIView?) {
|
||||
public override func willMoveToSuperview(newSuperview: UIView?) {
|
||||
if newSuperview == nil && isAnimating() {
|
||||
pauseAnimation()
|
||||
}
|
||||
@@ -361,24 +364,24 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
var progressThickness: CGFloat!
|
||||
var trackThickness: CGFloat!
|
||||
var trackColor: UIColor!
|
||||
var progressInsideFillColor = UIColor.clear
|
||||
var progressInsideFillColor: UIColor = UIColor.clearColor()
|
||||
var colorsArray: [UIColor]! {
|
||||
didSet {
|
||||
invalidateGradientCache()
|
||||
}
|
||||
}
|
||||
private var gradientCache: CGGradient?
|
||||
private var gradientCache: CGGradientRef?
|
||||
private var locationsCache: [CGFloat]?
|
||||
|
||||
private struct GlowConstants {
|
||||
private static let sizeToGlowRatio: CGFloat = 0.00015
|
||||
static func glowAmount(forAngle angle: Double, glowAmount: CGFloat, glowMode: KDCircularProgressGlowMode, size: CGFloat) -> CGFloat {
|
||||
static func glowAmountForAngle(angle: Double, glowAmount: CGFloat, glowMode: KDCircularProgressGlowMode, size: CGFloat) -> CGFloat {
|
||||
switch glowMode {
|
||||
case .forward:
|
||||
case .Forward:
|
||||
return CGFloat(angle) * size * sizeToGlowRatio * glowAmount
|
||||
case .reverse:
|
||||
case .Reverse:
|
||||
return CGFloat(360 - angle) * size * sizeToGlowRatio * glowAmount
|
||||
case .constant:
|
||||
case .Constant:
|
||||
return 360 * size * sizeToGlowRatio * glowAmount
|
||||
default:
|
||||
return 0
|
||||
@@ -386,11 +389,11 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override class func needsDisplay(forKey key: String) -> Bool {
|
||||
return key == "angle" ? true : super.needsDisplay(forKey: key)
|
||||
override class func needsDisplayForKey(key: String) -> Bool {
|
||||
return key == "angle" ? true : super.needsDisplayForKey(key)
|
||||
}
|
||||
|
||||
override init(layer: Any) {
|
||||
override init(layer: AnyObject) {
|
||||
super.init(layer: layer)
|
||||
let progressLayer = layer as! KDCircularProgressViewLayer
|
||||
radius = progressLayer.radius
|
||||
@@ -417,7 +420,7 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override func draw(in ctx: CGContext) {
|
||||
override func drawInContext(ctx: CGContext) {
|
||||
UIGraphicsPushContext(ctx)
|
||||
|
||||
let size = bounds.size
|
||||
@@ -427,63 +430,64 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
let trackLineWidth = radius * trackThickness
|
||||
let progressLineWidth = radius * progressThickness
|
||||
let arcRadius = max(radius - trackLineWidth/2, radius - progressLineWidth/2)
|
||||
ctx.addArc(center: CGPoint(x: width/2.0, y: height/2.0), radius: arcRadius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: false)
|
||||
CGContextAddArc(ctx, width/2.0, height/2.0, arcRadius, 0, CGFloat(M_PI * 2), 0)
|
||||
trackColor.set()
|
||||
ctx.setStrokeColor(trackColor.cgColor)
|
||||
ctx.setFillColor(progressInsideFillColor.cgColor)
|
||||
ctx.setLineWidth(trackLineWidth)
|
||||
ctx.setLineCap(CGLineCap.butt)
|
||||
ctx.drawPath(using: .fillStroke)
|
||||
CGContextSetStrokeColorWithColor(ctx, trackColor.CGColor)
|
||||
CGContextSetFillColorWithColor(ctx, progressInsideFillColor.CGColor)
|
||||
CGContextSetLineWidth(ctx, trackLineWidth)
|
||||
CGContextSetLineCap(ctx, CGLineCap.Butt)
|
||||
CGContextDrawPath(ctx, .FillStroke)
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||
let reducedAngle = Utility.mod(angle, range: 360, minMax: (0, 360))
|
||||
|
||||
let imageCtx = UIGraphicsGetCurrentContext()
|
||||
let reducedAngle = Utility.mod(value: angle, range: 360, minMax: (0, 360))
|
||||
let fromAngle = Conversion.degreesToRadians(value: CGFloat(-startAngle))
|
||||
let toAngle = Conversion.degreesToRadians(value: CGFloat((clockwise == true ? -reducedAngle : reducedAngle) - startAngle))
|
||||
|
||||
imageCtx?.addArc(center: CGPoint(x: width/2.0, y: height/2.0), radius: arcRadius, startAngle: fromAngle, endAngle: toAngle, clockwise: clockwise)
|
||||
|
||||
let glowValue = GlowConstants.glowAmount(forAngle: reducedAngle, glowAmount: glowAmount, glowMode: glowMode, size: width)
|
||||
if glowValue > 0 {
|
||||
imageCtx?.setShadow(offset: CGSize.zero, blur: glowValue, color: UIColor.black.cgColor)
|
||||
if let imageCtx = UIGraphicsGetCurrentContext(){
|
||||
let fromAngle = Conversion.degreesToRadians(CGFloat(-startAngle))
|
||||
let toAngle = Conversion.degreesToRadians(CGFloat((clockwise == true ? -reducedAngle : reducedAngle) - startAngle))
|
||||
|
||||
CGContextAddArc(imageCtx, width/2.0, height/2.0, arcRadius, fromAngle, toAngle, clockwise == true ? 1 : 0)
|
||||
|
||||
let glowValue = GlowConstants.glowAmountForAngle(reducedAngle, glowAmount: glowAmount, glowMode: glowMode, size: width)
|
||||
if glowValue > 0 {
|
||||
CGContextSetShadowWithColor(imageCtx, CGSizeZero, glowValue, UIColor.blackColor().CGColor)
|
||||
}
|
||||
CGContextSetLineCap(imageCtx, roundedCorners == true ? .Round : .Butt)
|
||||
CGContextSetLineWidth(imageCtx, progressLineWidth)
|
||||
CGContextDrawPath(imageCtx, .Stroke)
|
||||
|
||||
guard let currentGraphicsContext = UIGraphicsGetCurrentContext() else{
|
||||
return
|
||||
}
|
||||
|
||||
if let drawMask: CGImageRef = CGBitmapContextCreateImage(currentGraphicsContext){
|
||||
UIGraphicsEndImageContext()
|
||||
CGContextSaveGState(ctx)
|
||||
CGContextClipToMask(ctx, bounds, drawMask)
|
||||
}else{
|
||||
UIGraphicsEndImageContext()
|
||||
}
|
||||
}
|
||||
|
||||
let linecap: CGLineCap = roundedCorners == true ? .round : .butt
|
||||
imageCtx?.setLineCap(linecap)
|
||||
imageCtx?.setLineWidth(progressLineWidth)
|
||||
imageCtx?.drawPath(using: .stroke)
|
||||
|
||||
let drawMask: CGImage = UIGraphicsGetCurrentContext()!.makeImage()!
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
ctx.saveGState()
|
||||
ctx.clip(to: bounds, mask: drawMask)
|
||||
|
||||
//Gradient - Fill
|
||||
if !lerpColorMode && colorsArray.count > 1 {
|
||||
let rgbColorsArray: [UIColor] = colorsArray.map { color in // Make sure every color in colors array is in RGB color space
|
||||
if color.cgColor.numberOfComponents == 2 {
|
||||
if let whiteValue = color.cgColor.components?[0] {
|
||||
return UIColor(red: whiteValue, green: whiteValue, blue: whiteValue, alpha: 1.0)
|
||||
} else {
|
||||
return UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
|
||||
}
|
||||
if CGColorGetNumberOfComponents(color.CGColor) == 2 {
|
||||
let whiteValue = CGColorGetComponents(color.CGColor)[0]
|
||||
return UIColor(red: whiteValue, green: whiteValue, blue: whiteValue, alpha: 1.0)
|
||||
} else {
|
||||
return color
|
||||
}
|
||||
}
|
||||
|
||||
let componentsArray = rgbColorsArray.flatMap { color -> [CGFloat] in
|
||||
guard let components = color.cgColor.components else { return [] }
|
||||
let components: UnsafePointer<CGFloat> = CGColorGetComponents(color.CGColor)
|
||||
return [components[0], components[1], components[2], 1.0]
|
||||
}
|
||||
|
||||
drawGradientWith(context: ctx, componentsArray: componentsArray)
|
||||
drawGradientWithContext(ctx, componentsArray: componentsArray)
|
||||
} else {
|
||||
var color: UIColor?
|
||||
if colorsArray.isEmpty {
|
||||
color = UIColor.white
|
||||
color = UIColor.whiteColor()
|
||||
} else if colorsArray.count == 1 {
|
||||
color = colorsArray[0]
|
||||
} else {
|
||||
@@ -494,35 +498,35 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
for i in 1...steps {
|
||||
let fi = CGFloat(i)
|
||||
if (t <= fi * step || i == steps) {
|
||||
let colorT = Utility.inverseLerp(value: t, minMax: ((fi - 1) * step, fi * step))
|
||||
color = Utility.colorLerp(value: colorT, minMax: (colorsArray[i - 1], colorsArray[i]))
|
||||
let colorT = Utility.inverseLerp(t, minMax: ((fi - 1) * step, fi * step))
|
||||
color = Utility.colorLerp(colorT, minMax: (colorsArray[i - 1], colorsArray[i]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let color = color {
|
||||
fillRectWith(context: ctx, color: color)
|
||||
fillRectWithContext(ctx, color: color)
|
||||
}
|
||||
}
|
||||
ctx.restoreGState()
|
||||
CGContextRestoreGState(ctx)
|
||||
UIGraphicsPopContext()
|
||||
}
|
||||
|
||||
private func fillRectWith(context: CGContext!, color: UIColor) {
|
||||
context.setFillColor(color.cgColor)
|
||||
context.fill(bounds)
|
||||
private func fillRectWithContext(ctx: CGContext!, color: UIColor) {
|
||||
CGContextSetFillColorWithColor(ctx, color.CGColor)
|
||||
CGContextFillRect(ctx, bounds)
|
||||
}
|
||||
|
||||
private func drawGradientWith(context: CGContext!, componentsArray: [CGFloat]) {
|
||||
private func drawGradientWithContext(ctx: CGContext!, componentsArray: [CGFloat]) {
|
||||
let baseSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let locations = locationsCache ?? gradientLocationsFor(colorCount: componentsArray.count/4, gradientWidth: bounds.size.width)
|
||||
let locations = locationsCache ?? gradientLocationsForColorCount(componentsArray.count/4, gradientWidth: bounds.size.width)
|
||||
let gradient: CGGradient
|
||||
|
||||
if let cachedGradient = gradientCache {
|
||||
gradient = cachedGradient
|
||||
} else {
|
||||
guard let cachedGradient = CGGradient(colorSpace: baseSpace, colorComponents: componentsArray, locations: locations, count: componentsArray.count/4) else {
|
||||
guard let cachedGradient = CGGradientCreateWithColorComponents(baseSpace, componentsArray, locations,componentsArray.count / 4) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -531,18 +535,18 @@ public class KDCircularProgress: UIView, CAAnimationDelegate {
|
||||
}
|
||||
|
||||
let halfX = bounds.size.width / 2.0
|
||||
let floatPi = CGFloat.pi
|
||||
let floatPi = CGFloat(M_PI)
|
||||
let rotateSpeed = clockwise == true ? gradientRotateSpeed : gradientRotateSpeed * -1
|
||||
let angleInRadians = Conversion.degreesToRadians(value: rotateSpeed! * CGFloat(angle) - 90)
|
||||
let angleInRadians = Conversion.degreesToRadians(rotateSpeed * CGFloat(angle) - 90)
|
||||
let oppositeAngle = angleInRadians > floatPi ? angleInRadians - floatPi : angleInRadians + floatPi
|
||||
|
||||
let startPoint = CGPoint(x: (cos(angleInRadians) * halfX) + halfX, y: (sin(angleInRadians) * halfX) + halfX)
|
||||
let endPoint = CGPoint(x: (cos(oppositeAngle) * halfX) + halfX, y: (sin(oppositeAngle) * halfX) + halfX)
|
||||
|
||||
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: .drawsBeforeStartLocation)
|
||||
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, .DrawsBeforeStartLocation)
|
||||
}
|
||||
|
||||
private func gradientLocationsFor(colorCount: Int, gradientWidth: CGFloat) -> [CGFloat] {
|
||||
private func gradientLocationsForColorCount(colorCount: Int, gradientWidth: CGFloat) -> [CGFloat] {
|
||||
if colorCount == 0 || gradientWidth == 0 {
|
||||
return []
|
||||
} else {
|
||||
|
||||
@@ -169,13 +169,11 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = "Kaan Dedeoglu";
|
||||
TargetAttributes = {
|
||||
BC9E75851A8CE4A500B1DF3D = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
DevelopmentTeam = N7Y3X223PM;
|
||||
DevelopmentTeamName = "Kaan Dedeoglu";
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
BC9E759A1A8CE4A500B1DF3D = {
|
||||
@@ -293,10 +291,8 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -339,7 +335,6 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@@ -359,9 +354,8 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = KDCircularProgressExample/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -371,10 +365,8 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = KDCircularProgressExample/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -392,9 +384,8 @@
|
||||
);
|
||||
INFOPLIST_FILE = KDCircularProgressExampleTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 2.3;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KDCircularProgressExample.app/KDCircularProgressExample";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -409,10 +400,8 @@
|
||||
);
|
||||
INFOPLIST_FILE = KDCircularProgressExampleTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 2.3;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KDCircularProgressExample.app/KDCircularProgressExample";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
+5
-8
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0610"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -37,10 +37,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -62,18 +62,15 @@
|
||||
ReferencedContainer = "container:KDCircularProgressExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
@@ -89,10 +86,10 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
func applicationWillResignActive(application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
func applicationDidEnterBackground(application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
func applicationWillEnterForeground(application: UIApplication) {
|
||||
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
func applicationDidBecomeActive(application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
func applicationWillTerminate(application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@@ -22,19 +22,19 @@ class ViewController: UIViewController {
|
||||
progress.clockwise = true
|
||||
progress.gradientRotateSpeed = 2
|
||||
progress.roundedCorners = false
|
||||
progress.glowMode = .forward
|
||||
progress.glowMode = .Forward
|
||||
progress.glowAmount = 0.9
|
||||
progress.setColors(colors: UIColor.cyan ,UIColor.white, UIColor.magenta, UIColor.white, UIColor.orange)
|
||||
progress.setColors(UIColor.cyanColor() ,UIColor.whiteColor(), UIColor.magentaColor(), UIColor.whiteColor(), UIColor.orangeColor())
|
||||
progress.center = CGPoint(x: view.center.x, y: view.center.y + 25)
|
||||
view.addSubview(progress)
|
||||
}
|
||||
|
||||
@IBAction func sliderDidChangeValue(_ sender: UISlider) {
|
||||
@IBAction func sliderDidChangeValue(sender: UISlider) {
|
||||
progress.angle = Double(sender.value)
|
||||
}
|
||||
|
||||
@IBAction func animateButtonTapped(_ sender: UIButton) {
|
||||
progress.animate(fromAngle: 0, toAngle: 360, duration: 5) { completed in
|
||||
@IBAction func animateButtonTapped(sender: UIButton) {
|
||||
progress.animateFromAngle(0, toAngle: 360, duration: 5) { completed in
|
||||
if completed {
|
||||
print("animation stopped, completed")
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>com.kaandedeoglu.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ class KDCircularProgressExampleTests: XCTestCase {
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure() {
|
||||
self.measureBlock() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,10 @@
|
||||
[](http://cocoapods.org/pods/KDCircularProgress)
|
||||
[](http://cocoapods.org/pods/KDCircularProgress)
|
||||
|
||||
>
|
||||
`KDCircularProgress` master branch is now compatible with Swift 3. Check Swift 2 & Swift 2.3 branches for older versions.
|
||||
|
||||
|
||||
`KDCircularProgress` is a circular progress view written in Swift. It makes it possible to have gradients in the progress view, along with glows and animations.
|
||||
`KDCircularProgress` is a circular progress view written in Swift. It makes it possible to have gradients in the progress view, along with glows and animations.
|
||||
|
||||
KDCircularProgress also has `IBInspectable` and `IBDesignable` support, so you can configure and preview inside the `Interface Builder`.
|
||||
|
||||
|
||||
Here's an example
|
||||
|
||||
[Youtube Link](http://youtu.be/iIdas72MXOg)
|
||||
|
||||
Reference in New Issue
Block a user