What does UIView.animate add to the layer, why is the model value already final, and what do beginFromCurrentState and performWithoutAnimation do?
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
frameduring 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
finishedmean? 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… →