CALayer(CAAnimation)动画属性绘制错误

问题描述 投票:0回答:1

我正在创建带有条形动画的条形图。我在视图下划线图层(这是我的自定义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()
        }
    }
}
ios swift drawing calayer caanimation
1个回答
0
投票

您应使用@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函数。因此,您应将UIGraphicsPushContextUIGraphicsPopContext替换为其对应的Core Graphics:

override func draw(in ctx: CGContext) {
    ctx.saveGState()
    ...
    ctx.restoreGState()
}
© www.soinside.com 2019 - 2024. All rights reserved.