我正在创建带有条形动画的条形图。我在视图下划线图层(这是我的自定义BarChartLayer)中绘制它们。该层负责所有图纸。
对于动画图表条,我使用可动画属性-procentAnimation。在此值的动画更改期间,在draw方法中,我计算了两个数组的值(oldValues,newValues)之间的步长。从oldValues到newValues的FIRST动画效果很好。在动画委托中进行动画处理后,我更改了twoArrays的值(temp = oldValue,oldValue = newValue,newValue = temp)。第二动画应看起来像反向动画。但是,在动画开始之后,条形图跳转到FIRST动画的开始位置,然后像FIRST动画一样连续进行动画,在补图图表条形图返回到SECOND动画的最终位置之后。在debug mod中,我发现,当我添加第二个动画时,属性oldValues的值会保持正确,但newValues会保持正确,但是当动画在方法draw(in :)中开始时,oldValues的值会导致newValues属性跳回原来的状态在第一个动画期间。
是错误,还是某些我不理解的机制?如何解决这个问题?
class BarChartLayer: CALayer {
var oldValues: [CGFloat] = [45, 34, 12, 88, 17, 77, 88, 10] {
didSet {
debugPrint("oldValues set")
}
}
var newValues: [CGFloat] = [92, 72, 64, 32, 28, 15, 11, 33] {
didSet {
debugPrint("newValues set")
}
}
var numberOfBars: CGFloat = 8
var space: CGFloat = 10
@objc var procentAnimation : CGFloat = 0
override class func needsDisplay(forKey key: String) -> Bool {
if key == #keyPath(procentAnimation) {
return true
}
return super.needsDisplay(forKey:key)
}
func animateBarsChange() {
let ba = CABasicAnimation(keyPath:#keyPath(BarChartLayer.procentAnimation))
procentAnimation = 100
ba.duration = 4.0
ba.fromValue = 0
ba.delegate = self
add(ba, forKey:nil)
}
func barWidth(rect: CGRect) -> CGFloat {
return (rect.width - CGFloat(space * (numberOfBars - 1))) / CGFloat(numberOfBars)
}
func procentRanndomizer(rect: CGRect) -> CGFloat {
return CGFloat.random(in: 1...100) * rect.height / 100
}
func makeProcentFor(value: CGFloat, rect: CGRect) -> CGFloat {
return value * rect.height / 100
}
func additionalHeight(index: Int) -> CGFloat {
return (newValues[index] - oldValues[index]) / 100
}
func makeRandomArr() -> [CGFloat] {
var arr = [CGFloat]()
for _ in 0..<8 {
arr.append(CGFloat.random(in: 1...100))
}
return arr
}
override func draw(in ctx: CGContext) {
UIGraphicsPushContext(ctx)
let con = ctx
let rect = self.bounds
con.addRect(rect)
con.setFillColor(UIColor.gray.cgColor)
con.fillPath()
var px:CGFloat = 0
let bw = barWidth(rect: rect)
debugPrint("OLD - \(oldValues)")
debugPrint("NEW - \(newValues)")
let oldValues = self.oldValues
for i in 0..<Int(numberOfBars) {
px = CGFloat(i) * (bw + space)
let h = oldValues[i] + procentAnimation * additionalHeight(index: i)
con.addRect(CGRect(x: px, y: 0, width: bw, height: makeProcentFor(value: h, rect: rect)))
}
con.setFillColor(UIColor.green.cgColor)
con.fillPath()
UIGraphicsPopContext()
}
}
extension BarChartLayer: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag {
let temp = self.oldValues
self.oldValues = self.newValues
self.newValues = temp//makeRandomArr()
self.procentAnimation = 0
debugPrint("------------------------------------")
setNeedsDisplay()
}
}
}
您应使用@NSManaged
标记自定义动画属性,因为Core Animation会创建模型图层的副本以进行渲染。 @NSManaged
不允许使用默认值。因此,您应该改写defaultValue(forKey:)
。
@NSManaged var procentAnimation : CGFloat
override class func defaultValue(forKey inKey: String) -> Any? {
return inKey == "procentAnimation" ? 0.0 : super.defaultValue(forKey: inKey)
}
您也不应在CALayer实现中使用UIKit函数。因此,您应将UIGraphicsPushContext
和UIGraphicsPopContext
替换为其对应的Core Graphics:
override func draw(in ctx: CGContext) {
ctx.saveGState()
...
ctx.restoreGState()
}