It's unfortunate, but Core Animation doesn't expose its internal computational model for its animation timing. However, what has worked really well for me is to use Core Animation to do the work!
CALayer to serve as an evaluator((0.0, 0.0), (1.0, 1.0))isHidden to truespeed to 0.0When you want to evaluate any CAMediaTimingFunction, create a reference animation:
let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x")
basicAnimation.duration = 1.0
basicAnimation.timingFunction = timingFunction
basicAnimation.fromValue = 0.0
basicAnimation.toValue = containerLayer.bounds.width
referenceLayer.add(basicAnimation, forKey: "evaluatorAnimation")
Set the reference layer's timeOffset to whatever normalized input value (i.e., between 0.0 and 1.0) you want to evaluate:
referenceLayer.timeOffset = 0.3 // 30% into the animation
Ask for the reference layer's presentation layer, and get its current bounds origin x value:
if let presentationLayer = referenceLayer.presentation() as CALayer? {
let evaluatedValue = presentationLayer.bounds.origin.x / containerLayer.bounds.width
}
Basically, you're using Core Animation to run an animation for an invisible layer. But the layer's speed is 0.0, so it won't progress the animation at all. Using timeOffset, we can manually adjust the current position of the animation then get its presentation layer's x position. This represents the current perceived value of that property as driven by the animation.
It's a little unconventional, but there's nothing hacky about it. It's as faithful a representation of the output value of a CAMediaTimingFunction as you can get because Core Animation is actually using it.
The only thing to be aware of is that presentation layers are close approximations of the values presented on screen. Core Animation makes no guarantees as to their accuracy, but in all my years of using Core Animation, I've never seen it be inaccurate. Still, if your application requires absolute accuracy, it's possible this technique might not be the best.