CALayer (CAAnimation) 动画属性绘制错误
Posted
技术标签:
【中文标题】CALayer (CAAnimation) 动画属性绘制错误【英文标题】:CALayer (CAAnimation) animatable property drawing bug 【发布时间】:2019-12-04 22:10:20 【问题描述】:我正在创建带有条形动画的条形图。我在视图下划线层(这是我的自定义BarChartLayer
)中绘制它们。这一层负责所有的绘图。
对于动画图表条,我使用名为 procentAnimation
的动画属性。在这个值的动画变化期间,在 draw 方法中,我计算了两个数组(oldValues
,newValues
)的值之间的步长。
从 oldValues 到 newValues 的第一个动画效果很好。在动画委托中的动画之后,我交换了两个数组的值。
第二个动画应该看起来像反向动画。但是动画开始后,条形图跳到第一个动画开始位置,然后动画像第一个动画一样连续,完成后图表条形图跳回到第二个动画的最终位置。
在调试模式下,我发现,当我添加第二个动画时,属性 oldValues
和 newValues
的值保持正确,但是当动画在方法 draw(in:)
中开始时,oldValues
和 @987654329 的值@ 跳回到第一个动画期间的状态。
这是一个错误还是有一些我不明白的机制?如何解决这个问题?
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()
【问题讨论】:
【参考方案1】:您应该使用 @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()
【讨论】:
以上是关于CALayer (CAAnimation) 动画属性绘制错误的主要内容,如果未能解决你的问题,请参考以下文章