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

标签 ios swift drawing calayer caanimation

我正在创建带条形动画的条形图。我将它们绘制在 View 下划线层中(这是我的自定义 BarChartLayer )。该层负责所有绘图。

对于动画图表栏,我使用名为 procentAnimation 的可动画属性.在该值的动画变化期间,在绘制方法中,我计算两个数组的值之间的步长( oldValuesnewValues )。

从 oldValues 到 newValues 的第一个动画效果很好。在动画委​​托中执行动画之后,我交换两个数组的值。

第二个动画应该看起来像反向动画。但是在动画开始后,条形图跳到第一个动画开始位置,然后动画像第一个动画一样连续,完成后图表条形图跳回到第二个动画的最终位置。

在 Debug模式下,我发现,当我添加第二个动画时,属性值 oldValuesnewValues保持正确,但是当动画在方法 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 函数。因此,您应该将 UIGraphicsPushContextUIGraphicsPopContext 替换为 Core Graphics 对应项:

override func draw(in ctx: CGContext) {
    ctx.saveGState()
    ...
    ctx.restoreGState()
}

关于ios - CALayer (CAAnimation) 可动画属性绘制错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59185344/

相关文章:

ios - 删除所需的初始值设定项

ios - 如何在真实 iPhone 上测试 NativeScript 应用程序

swift - 如何禁用输入框的自动更正?

swift - 无法将通过终端输入的字符串与 Swift 中的另一个字符串进行比较

c# - 使用撤消和重做功能绘制形状和字符串

ios - 检测用户是否在给定线上绘制

IOS应用程序图标名称仍然需要@2x?

ios - 面部识别是如何内置到 Core ML 视觉框架中的

javascript - 使用 XOR 在 JavaScript/HTML5 中绘制以删除旧 Sprite

ios - 我什么时候以及为什么要使用 ARC 将局部变量声明为 __weak?