What does UIView.animate add to the layer, why is the model value already final, and what do beginFromCurrentState and performWithoutAnimation do?

9.10.1 · L5 · Interview

Predict first

What does a UIView animation add to the layer, and what are the completion flags of two overlapping animations?

import UIKit

let win = UIWindow(frame: CGRect(x: 0, y: 0, width: 300, height: 600)); win.isHidden = false
let v = UIView(frame: CGRect(x: -25, y: -25, width: 50, height: 50)); win.addSubview(v)
print(v.layer.action(forKey: "position") == nil)
UIView.animate(withDuration: 1) { v.center = CGPoint(x: 200, y: 200) }
let a = v.layer.animation(forKey: "position") as! CABasicAnimation
print(v.layer.animationKeys()!, v.layer.position, a.isAdditive, (a.fromValue as! NSValue).cgPointValue, (a.toValue as! NSValue).cgPointValue)
UIView.performWithoutAnimation { v.alpha = 0.5 }
print(v.layer.animationKeys()!)
let u = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)); win.addSubview(u)
var finished: [Bool] = []
UIView.animate(withDuration: 0.2, animations: { u.alpha = 0 }, completion: { finished.append($0) })
UIView.animate(withDuration: 0.2, delay: 0, options: [.beginFromCurrentState], animations: { u.alpha = 1 }, completion: { finished.append($0) })
RunLoop.main.run(until: Date().addingTimeInterval(0.6))
print(finished, u.alpha, u.layer.animationKeys() == nil)
  • true, then ["position"] (200.0, 200.0) true (-200.0, -200.0) (0.0, 0.0), then ["position"], then [false, true] 1.0 true
  • false, then ["position"] (-25.0, -25.0) false (0.0, 0.0) (200.0, 200.0), then ["position", "opacity"], then [true, true] 1.0 true
  • true, then ["position"] (200.0, 200.0) true (-200.0, -200.0) (0.0, 0.0), then ["position", "opacity"], then [true, false] 0.0 false

Mental model. The view is teleported to its destination at once and a spring-loaded ghost offset eases the picture back to it.

A UIView property change outside an animation block has no action, so it jumps (the layer’s action(forKey:) returns nothing). Inside UIView.animate, UIKit turns actions back on and the change creates a CABasicAnimation on the view’s layer. The simulator check shows its shape: for center moving from (0, 0) to (200, 200), the layer’s position is already (200, 200), and the animation on key position is additive with fromValue (−200, −200) (old minus new) and toValue (0, 0). The visible position is the model value plus a decaying offset. Because the model is final immediately, view.frame already reports the destination mid-flight, and the presentation() layer is what is on screen.

This is also why interruption works well. Starting a second animation on the same property adds a second additive animation on top of the first, and the first one’s completion runs with finished == false while the second finishes true, as the check shows. The option .beginFromCurrentState makes the new animation start from the presentation value rather than the model value; without it, a view that was mid-flight can jump visually when you reverse it. UIView.performWithoutAnimation { } runs a change with actions off, which is how you reposition a view instantly inside code that is otherwise animating; UIView.areAnimationsEnabled is the global switch. The old approach of reading the model value in a gesture handler is wrong for the same reason: use layer.presentation() for the on-screen value.

Outside a block there is no action; inside, an additive animation from old-minus-new to zero is added while the model is already final; and the interrupted first animation reports false.

Common traps

  • Reading frame during an animation and expecting the on-screen value. It shows the destination; read the presentation layer.
  • Reversing an animation without .beginFromCurrentState. The view can jump before moving back.

They might follow up with

  • What does the completion’s finished mean? False if another animation interrupted or removed it.
  • How do you make a change without animating it inside an animation block? performWithoutAnimation.

Related questions

  • 9.4.1 How do implicit and explicit animations differ in Core Animation, and how does CATransaction fit in?
  • 9.4.2 How do UIView.animate, UIViewPropertyAnimator and SwiftUI’s animations relate, and when do you pick each?
  • 9.2.2 What are the model, presentation and render layer trees, and how do the layer geometry properties relate?

How do spring parameters work: mass, stiffness, damping, duration and… →