animated layer property does not work with animateKeyframes

I have added a custom property to a subclass of CALayer in order to animate the layer drawing. The property is correctly animated when I run a UIView.animate but NOT when running UIView.animateKeyframes. Any ideas? The full sample project is available here.


This kind of animation works:

UIView.animate(withDuration: self.duration, animations: {
   self.transitionView.progress = 0
})


And that DOES NOT works:


UIView.animateKeyframes(withDuration: self.duration, delay: 0, options: .calculationModeCubic, animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1) {
     self.transitionView.progress = 0
})


Here is the layer definition :


class AnimatedLayer: CALayer {
    @NSManaged var progress: CGFloat
    
    // Whenever a new presentation layer is created, this function is called and makes a COPY of the object.
    override init(layer: Any) {
        super.init(layer: layer)
        if let layer = layer as? AnimatedLayer {
            progress = layer.progress
        }
    }
    
    override init() {
        super.init()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override class func needsDisplay(forKey key: String) -> Bool {
        if isAnimationKeySupported(key) {
            return true
        }
        return super.needsDisplay(forKey: key)
    }
    
    override func action(forKey event: String) -> CAAction? {
        
        if AnimatedLayer.isAnimationKeySupported(event) {
            // Copy animation context and mutate as needed
            guard let animation = currentAnimationContext(in: self)?.copy() as? CABasicAnimation else {
                setNeedsDisplay()
                return nil
            }
            
            animation.keyPath = event
            if let presentation = presentation() {
                animation.fromValue = presentation.value(forKeyPath: event)
            }
            animation.toValue = nil
            return animation
        }
        
        return super.action(forKey: event)
    }
    
    private class func isAnimationKeySupported(_ key: String) -> Bool {
        return key == #keyPath(progress)
    }
    
    private func currentAnimationContext(in layer: CALayer) -> CABasicAnimation? {
        /// The UIView animation implementation is private, so to check if the view is animating and
        /// get its property keys we can use the key "backgroundColor" since its been a property of
        /// UIView which has been forever and returns a CABasicAnimation.
        return action(forKey: #keyPath(backgroundColor)) as? CABasicAnimation
    }
}


Ands the view code is :


class DayTransitionView: UIView {
    
    override class var layerClass: AnyClass {
        return AnimatedLayer.self
    }
    
    var progressLayer: AnimatedLayer {
        return layer as! AnimatedLayer
    }
    
    @objc dynamic var progress: CGFloat {
        set { progressLayer.progress = newValue }
        get { return progressLayer.presentation()?.progress ?? progressLayer.progress }
    }
    
    // if not redefined draw:layer is not called !
    override func draw(_ rect: CGRect) { }
    
    override func draw(_ layer: CALayer, in ctx: CGContext) {
        
        UIGraphicsPushContext(ctx);
        
        // background color
        let backgroundPath: CGPath = UIBezierPath(roundedRect: layer.bounds, byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight] , cornerRadii: .zero).cgPath
        ctx.addPath(backgroundPath)
        ctx.setFillColor(UIColor.green.cgColor)
        ctx.closePath()
        ctx.fillPath()
        
        // background color
        let fillHeight = self.progressive(layer.bounds.origin.y, layer.bounds.origin.y + layer.bounds.size.height)
        let fillRect = CGRect(x: layer.bounds.origin.x, y: layer.bounds.origin.y + layer.bounds.size.height - fillHeight, width: layer.bounds.size.width, height: fillHeight)
        let fillPath: CGPath = UIBezierPath(roundedRect: fillRect, byRoundingCorners: [.topLeft, .topRight, .bottomLeft, .bottomRight] , cornerRadii: .zero).cgPath
        ctx.addPath(fillPath)
        ctx.setFillColor(UIColor.red.cgColor)
        ctx.closePath()
        ctx.fillPath()
        
        UIGraphicsPopContext();
    }
    
    func progressive(_ from: CGFloat, _ to: CGFloat) -> CGFloat {
        return from + self.progress * (to - from)
    }
}
animated layer property does not work with animateKeyframes
 
 
Q