I have a UILabel
that I am attempting to scale and translate, and I want to animate this. I am doing this by setting its transform
in a UIView.animate
block. When the animation is finished, I would like to set the view's transform
back to .identity
and update its frame so that it remains exactly where the CGAffineTransform
moved it. Pseudocode:
func animateMovement(label: UILabel,
newWidth: CGFloat,
newHeight: CGFloat,
newOriginX: CGFloat,
newOriginY: CGFloat)
{
UIView.animate(withDuration: duration, animations: {
label.transform = ??? // Something that moves it to the new location and new size
}) {
label.frame = ??? // The final frame from the above animation
label.transform = CGAffineTransform.identity
}
}
As to why I don't simply assign the new frame in the animation block: I have text inside the label that I want to scale with the animation, which is not possible when animating changing the frame instead of the transform.
I'm having coordinate space problems, which I can tell because after the animation it is not in the correct location (the label has the wrong origin).
Here is the answer I came up with. This will scale the contents through the duration of the animation and then reset the object to have the identity transform when finished.
static func changeFrame(label: UILabel,
toOriginX newOriginX: CGFloat,
toOriginY newOriginY: CGFloat,
toWidth newWidth: CGFloat,
toHeight newHeight: CGFloat,
duration: TimeInterval)
{
let oldFrame = label.frame
let newFrame = CGRect(x: newOriginX, y: newOriginY, width: newWidth, height: newHeight)
let translation = CGAffineTransform(translationX: newFrame.midX - oldFrame.midX,
y: newFrame.midY - oldFrame.midY)
let scaling = CGAffineTransform(scaleX: newFrame.width / oldFrame.width,
y: newFrame.height / oldFrame.height)
let transform = scaling.concatenating(translation)
UIView.animate(withDuration: duration, animations: {
label.transform = transform
}) { _ in
label.transform = .identity
label.frame = newFrame
}
}