Search code examples
iosswiftcalayercabasicanimation

Confused about the CAAction's run method


I'm trying to understand how this method run(forKey:object:arguments:) is operating exactly in the following code from Apple's documentation:

let delegate = LayerDelegate()
     
lazy var sublayer: CALayer = {
    let layer = CALayer()
    layer.delegate = self.delegate
    
    return layer
}()
     
func moveSublayer() {
    guard let action = sublayer.action(forKey: "moveRight") else {
        return
    }
    
    action.run(forKey: "transform", object: sublayer, arguments: nil) // this line
}
     
class LayerDelegate: NSObject, CALayerDelegate {
    func action(for layer: CALayer, forKey event: String) -> CAAction? {
        
        guard event == "moveRight" else {
            return nil
        }
        
        let animation = CABasicAnimation()
        animation.valueFunction = CAValueFunction(name: CAValueFunctionName.translateX)
        animation.fromValue = 1
        animation.toValue = 300
        animation.duration = 2
        
        return animation
    }
}

I'm particularly confused about the forKey parameter from action.run(forKey: "transform", object: sublayer, arguments: nil).

In the documentation, it's stated as:

The identifier of the action. The identifier may be a key or key path relative to anObject, an arbitrary external action, or one of the action identifiers defined in CALayer.

I understand that the animation list in the render tree is like a dictionary so you query the list with a key and you get a particular animation as a value, which is what happened in the example above. The sublayer queries the animation list with "moveRight":

guard let action = sublayer.action(forKey: "moveRight") else {
    return
}

And then the action is returned with the following method:

func action(for layer: CALayer, forKey event: String) -> CAAction? {
    guard event == "moveRight" else {
        return nil
    }
    
    let animation = CABasicAnimation()
    animation.valueFunction = CAValueFunction(name: kCAValueFunctionTranslateX)
    animation.fromValue = 1
    animation.toValue = 300
    animation.duration = 2
    
    return animation
}

But, what is this forKey: "transform" for? We've already queried the animation list with the "moveRight" key and got the value. Why do we need another key?

Also, what if I were to create a group animation combining transform and opacity or other non-transform property? What would I have to use instead of forKey: "transform"?


Solution

  • All CALayer properties are accessible by their string names via key-value coding. That fact is the basis of implicit layer animation and the entire CAAction mechanism.

    Okay, well, there is no moveRight property. "moveRight" is just a name we made up. You have to ask yourself how we are going to move right.

    In this example, we are going to do it by animating the layer's transform, in particular using a translate transform with an X component that goes from 1 to 300 (as you see in the action(forLayer) implementation). Hence we must use the "transform" key, because we want to animate the transform property.