I would like to (ab-)use Core Animation or even UIDynamics to generate animated values (floats) that I could use to control other parameters.
E.g. Having an animation with an ease in, I would like to control a color with an 'eased' value.
I would like to (ab-)use Core Animation or even UIDynamics to generate animated values (floats) that I could use to control other parameters.
E.g. Having an animation with an ease in, I would like to control a color with an 'eased' value.
Core Animation can certainly do this for you. You need to create a CALayer
subclass that has a new property you'd like to animate:
class CustomLayer: CALayer {
@NSManaged var colorPercentage: Float
override required init(layer: AnyObject) {
super.init(layer: layer)
if let layer = layer as? CustomLayer {
colorPercentage = layer.colorPercentage
} else {
colorPercentage = 0.0
}
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
}
override class func needsDisplay(forKey key: String) -> Bool {
var needsDisplay = super.needsDisplay(forKey: key)
if key == "colorPercentage" {
needsDisplay = true
}
return needsDisplay
}
override func action(forKey key: String) -> CAAction? {
var action = super.action(forKey: key)
if key == "colorPercentage" {
action = super.action(forKey: "opacity") // Create reference action from an existing CALayer key
if let action = action as? CABasicAnimation {
action.keyPath = key
action.fromValue = value(forKeyPath: key)
}
}
return action
}
override func display() {
guard let presentationLayer = presentation() else { return }
print("Current 'colorPercentage' value: \(presentationLayer.colorPercentage)")
// Called for every frame of an animation involving "colorPercentage"
}
}
(In Swift, @NSManaged
is a hack currently necessary for Core Animation to treat colorPercentage
as an Objective-C @dynamic
property.)
Following this pattern, you can create a custom layer that contains your own animatable properties. display()
will be called for every frame of the animation on the main thread, so you can respond to value changes over time however you'd like:
let animation = CABasicAnimation(keyPath: "colorPercentage")
animation.duration = 1.0
animation.fromValue = customLayer.colorPercentage
customLayer.colorPercentage = 1.0
customLayer.add(animation, forKey: "colorPercentageAnimation")
As a bonus, action(forKey:)
can be used to create an action from a "reference animation" that Core Animation knows about and configures it to work with your custom property. With this, it's possible to invoke CALayer
animations inside UIKit-style animation closures, which are much more convenient to use:
UIView.animate(withDuration: 1.0, animations: {
customLayer.colorPercentage = 1.0
})
My best guess is to use CADisplayLink. See this post. Changes of an animation are not observable by KVO.