Search code examples
iosswiftcore-animation

CABasicAnimation without interpolation values


Apple's documentation link states that:

The fromValue, byValue and toValue properties define the values being interpolated between. All are optional, and no more than two should be non-nil. The object type should match the type of the property being animated.

The interpolation values are used as follows:

  • All properties are nil. Interpolates between the previous value of keyPath in the target layer’s presentation layer and the current value of keyPath in the target layer’s presentation layer.

How can I make this work without specifying any of the fromValue, byValue, or toValue?

Here, I'm just animating the change of the cornerRadius property.

I know how to make this animation using toValue. But I just want to know if there is an even simpler code than this to achieve it:

import UIKit
import PlaygroundSupport

let rect = CGRect(x: 0,y: 0, width: 300, height: 400)
let view = UIView(frame: rect)
view.backgroundColor = UIColor.yellow
PlaygroundPage.current.liveView = view

let layer = CALayer()
layer.backgroundColor = UIColor.red.cgColor
layer.frame = CGRect(x: 75, y: 75, width: 150, height: 150)
view.layer.addSublayer(layer)

layer.cornerRadius = 15
let animation = CABasicAnimation(keyPath:#keyPath(CALayer.cornerRadius))
animation.toValue = layer.bounds.width / 2
animation.duration = 1
layer.add(animation, forKey: nil)

Solution

  • In some situations, where you have already set the layer's final value without animation, you can skip setting both the toValue and the fromValue in your CABasicAnimation, because the runtime gets the fromValue from the current value of the presentation layer and the toValue from the current value of the model layer.

    For example, in my own code, it turns out that I can reduce this full form of specifying an animation...

            let startValue = arrow.transform
            let endValue = CATransform3DRotate(startValue, .pi/4.0, 0, 0, 1)
            CATransaction.setDisableActions(true)
            arrow.transform = endValue
            let anim = CABasicAnimation(keyPath:#keyPath(CALayer.transform))
            anim.duration = 0.8
            let clunk = CAMediaTimingFunction(controlPoints:0.9, 0.1, 0.7, 0.9)
            anim.timingFunction = clunk
            anim.fromValue = startValue
            anim.toValue = endValue
            arrow.add(anim, forKey:nil)
    

    ...to this (notice that fromValue and toValue are omitted):

            CATransaction.setDisableActions(true)
            arrow.transform = CATransform3DRotate(arrow.transform, .pi/4.0, 0, 0, 1)
            let anim = CABasicAnimation(keyPath:#keyPath(CALayer.transform))
            anim.duration = 0.8
            let clunk = CAMediaTimingFunction(controlPoints:0.9, 0.1, 0.7, 0.9)
            anim.timingFunction = clunk
            arrow.add(anim, forKey:nil)
    

    However, I do not recommend that approach. It is much more reliable always to supply both the fromValue and the toValue. Yes, it's two more lines of code, but that is a small price to pay for avoiding confusion. Sometimes, clarity is simplicity.