iOS Animation: key value and animation proxy of Layer

Posted by ZibingsCoder on Thu, 21 Nov 2019 22:57:54 +0100

First, I will introduce the agent method of CAAnimation. CAAnimationDelegate has two alternative proxy methods:

func animationDidStart(_ anim: CAAnimation)
func animationDidStop(_ anim: CAAnimation, finished flag: Bool)

Cabasic animation inherits to CAAnimation. They are all written in Objective-C, and they follow the key value coding style, which means that you can use them as dictionaries and add new properties to them at run time.
We continue to use the cabasic animation object flyRight that we created earlier. For example:

flyRight.setValue("form", forKey: "name")

Now you can judge whether it is a copy of the current animation according to name in its proxy method. You can also assign a username.layer to it to keep references to a layer. for example

flyRight.setValue(username.layer, forKey: "layer")
flyRight.setValue(password.layer, forKey: "layer")

The whole code is as follows:

    let flyRight = CABasicAnimation(keyPath: "position.x")
    flyRight.fromValue = -view.bounds.size.width/2
    flyRight.toValue = view.bounds.size.width/2
    flyRight.duration = 0.5
    flyRight.delegate = self
    flyRight.setValue("form", forKey: "name")
    flyRight.setValue(heading.layer, forKey: "layer")
    heading.layer.add(flyRight, forKey: nil)//Copy a copy of flyRight to heading.layer

    flyRight.beginTime = CACurrentMediaTime() + 0.3
    flyRight.fillMode = kCAFillModeBoth
    flyRight.setValue(username.layer, forKey: "layer")
    username.layer.add(flyRight, forKey: nil)//Copy a copy of flyRight to username.layer

    flyRight.beginTime = CACurrentMediaTime() + 0.4
    flyRight.setValue(password.layer, forKey: "layer")
    password.layer.add(flyRight, forKey: nil)//Copy a copy of flyRight to password.layer

Proxy method implementation:

  func animationDidStop(_ anim: CAAnimation,
                        finished flag: Bool) {
    print("animation did finish")

    guard let name = anim.value(forKey: "name") as? String else {
      return
    }

    if name == "form" {
      //form field found

      let layer = anim.value(forKey: "layer") as? CALayer
      anim.setValue(nil, forKey: "layer")//Remove reference to layer when animation is complete

      let pulse = CABasicAnimation(keyPath: "transform.scale")//Start a new animation
      pulse.fromValue = 1.25
      pulse.toValue = 1.0
      pulse.duration = 0.25
      layer?.add(pulse, forKey: nil)
    }
  }

Example: add two animations to the label named info

let flyLeft = CABasicAnimation(keyPath: "position.x")
flyLeft.fromValue = info.layer.position.x +     
view.frame.size.width
flyLeft.toValue = info.layer.position.x
flyLeft.duration = 5.0
info.layer.add(flyLeft, forKey: "infoappear")

let fadeLabelIn = CABasicAnimation(keyPath: "opacity")
fadeLabelIn.fromValue = 0.2
fadeLabelIn.toValue = 1.0
fadeLabelIn.duration = 4.5
info.layer.add(fadeLabelIn, forKey: "fadein")

Two other methods are introduced

  1. info.layer.animationKeys(): we can get the key values of all animation s of the layer
  2. info.layer.removeAnimation(forKey: "infoapp ear"): remove the animation whose key is infoapp ear under the layer

If you want to perform multiple animations simultaneously on a single layer, you can also use CAAnimationGroup, which will be described later.