您也可以使用CAAnimation 到animate color。所以你可以点击那个链接。
您缺少的部分是计算需要根据当前值插值的from 和to 颜色。
假设您的进度视图将在某个时候更新界面以具有
var minimumValue: CGFloat = 0.0
var maximumValue: CGFloat = 100.0
var currentValue: CGFloat = 30.0 // 30%
然后使用这些值,您可以计算当前进度,0 和 1 之间的值应该定义颜色插值比例。计算它的基本数学应该是:
let progress = (currentValue-maximumValue)/(minimumValue-maximumValue) // TODO: handle division by zero. Handle current value out of bounds.
随着进度,您现在可以使用插入任何数值
func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
return values.from + (values.to - values.from)*scale
}
但颜色不是数值。在CAAnimation 的情况下,您应该将其分解为 4 个数值作为 RGBA 以保持一致性(至少我相信 CAAnimation 在 RGBA 空间中使用颜色插值)。就像一个注释;在许多情况下,当您在 HSV 空间而不是 RGB 中进行插值时,它看起来会更好。
所以插值颜色应该是这样的:
func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
return (r, g, b, a)
}
func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
return values.from + (values.to - values.from)*scale
}
func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
let startColorComponents = rgba(values.from)
let endColorComponents = rgba(values.to)
return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
}
这些应该是您制作动画所需的所有组件,我希望它们足以让您走上正轨。我个人喜欢有更多的控制权,而且我喜欢避免使用CAAnimation 甚至形状图层等工具。所以这就是我将如何完成你的任务:
class ViewController: UIViewController {
@IBOutlet private var progressView: ProgressView?
override func viewDidLoad() {
super.viewDidLoad()
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
}
@objc private func onTap() {
guard let progressView = progressView else { return }
progressView.animateValue(to: .random(in: progressView.minimumValue...progressView.maximumValue), duration: 0.3)
}
}
@IBDesignable class ProgressView: UIView {
@IBInspectable var minimumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
@IBInspectable var maximumValue: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
@IBInspectable var value: CGFloat = 0.0 { didSet { setNeedsDisplay() } }
@IBInspectable var colorAtZeroProgress: UIColor = .black { didSet { setNeedsDisplay() } }
@IBInspectable var colorAtFullProgress: UIColor = .black { didSet { setNeedsDisplay() } }
@IBInspectable var lineWidth: CGFloat = 10.0 { didSet { setNeedsDisplay() } }
private var currentTimer: Timer?
func animateValue(to: CGFloat, duration: TimeInterval) {
currentTimer?.invalidate()
let startTime = Date()
let startValue = self.value
currentTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true, block: { timer in
let progress = CGFloat(max(0.0, min(Date().timeIntervalSince(startTime)/duration, 1.0)))
if progress >= 1.0 {
// End of animation
timer.invalidate()
}
self.value = startValue + (to - startValue)*progress
})
}
override func draw(_ rect: CGRect) {
super.draw(rect)
let progress = max(0.0, min((value-minimumValue)/(maximumValue-minimumValue), 1.0))
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = min(bounds.width, bounds.height)*0.5 - lineWidth*0.5
let startAngle: CGFloat = -.pi*2.0
let endAngle: CGFloat = startAngle + .pi*2.0*progress
let circularPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
let interpolatedColor: UIColor = {
func rgba(_ color: UIColor) -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
return (r, g, b, a)
}
func interpolate(_ values: (from: CGFloat, to: CGFloat), scale: CGFloat) -> CGFloat {
return values.from + (values.to - values.from)*scale
}
func interpolate(_ values: (from: UIColor, to: UIColor), scale: CGFloat) -> UIColor {
let startColorComponents = rgba(values.from)
let endColorComponents = rgba(values.to)
return .init(red: interpolate((startColorComponents.r, endColorComponents.r), scale: scale),
green: interpolate((startColorComponents.g, endColorComponents.g), scale: scale),
blue: interpolate((startColorComponents.b, endColorComponents.b), scale: scale),
alpha: interpolate((startColorComponents.a, endColorComponents.a), scale: scale))
}
return interpolate((self.colorAtZeroProgress, self.colorAtFullProgress), scale: progress)
}()
interpolatedColor.setStroke()
circularPath.lineCapStyle = .round
circularPath.lineWidth = lineWidth
circularPath.stroke()
}
}
随意使用它并随意修改它。