I'm seeing some unexpected, inconsistent behavior when applying transforms in steps as opposed to applying them at once and I'd like to know why.
Say we have a label that we'd like to translate to the right 100
and down 50
and then scale up to 1.5
times the original size. So there are two transformations:
- Translation
- Scale
And say that we are experimenting with two different animations:
- Perform the translation and scale in parallel
- Perform the translation, then perform the scale in sequence
In the first animation you might do something like this:
UIView.animate(withDuration: 5, animations: {
label.transform = label.transform.translatedBy(x: 100, y: 50).scaledBy(x: 1.5, y: 1.5)
}, completion: nil)
And everything behaves how you'd expect. The label translates and scales smoothly at the same time.
In the second animation:
UIView.animate(withDuration: 5, animations: {
label.transform = label.transform.translatedBy(x: 100, y: 50)
}, completion: { _ in
UIView.animate(withDuration: 5, animations: {
label.transform = label.transform.scaledBy(x: 1.5, y: 1.5)
}, completion: nil)
})
The label translates correctly, and then boom, it jumps unexpectedly and then starts to scale.
What causes that sudden, unexpected jump? From inspecting the matrices for each transform (the parallelized and the sequential transforms) the values are the same, as would be expected.
Parallelized Animation
transform before translate and scale: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0)
translate and scale transform: CGAffineTransform(a: 1.5, b: 0.0, c: 0.0, d: 1.5, tx: 100.0, ty: 50.0)
transform after translate and scale: CGAffineTransform(a: 1.5, b: 0.0, c: 0.0, d: 1.5, tx: 100.0, ty: 50.0)
Sequential Animation
transform before translation: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0)
translation transform: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 100.0, ty: 50.0)
transform after translation: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 100.0, ty: 50.0)
transform before scale: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 100.0, ty: 50.0)
scale transform: CGAffineTransform(a: 1.5, b: 0.0, c: 0.0, d: 1.5, tx: 100.0, ty: 50.0)
transform after scale: CGAffineTransform(a: 1.5, b: 0.0, c: 0.0, d: 1.5, tx: 100.0, ty: 50.0)
So what is it that causes the sudden jump?
You need to understand how animation works in iOS. Your
animation
closure block runs right away and the final values are assigned to the object straight away (This is one of the most important points that a lot of people forget). All theanimation
block does is that it makes it appear to take that much time. Let me elaborate with an example.With that in mind, let's look at the second example that you posted
The first animation runs and translates your view right away. It only takes 5 seconds to move there but your view's x & y values have already changed. Upon completion, you scale it resulting in a weird behaviour.