I have two UILabel
s. A bigger on the left side of the screen and a smaller on the right side.
I'm trying to use CGAffineTransform
to animate moving the smaller label into the place of the bigger one and scale it to the same size, and move the bigger one out of the screen.
I'm not actually moving the labels, after the animation is complete, I change the text property on the labels and I set their transforms to identity
.
My problem is that I don't know how to calculate the exact x and y values that I have to translate my smaller label. I think the values I have are not accurate because I scale the label the same time I translate it, and the tx
and ty
values are calculated with the non-scaled size of the smaller label.
What I do currently:
tx
: the width of the bigger label + the distance between it and the smaller label, ty
: the distance between the centres of the two labels on the y axis
There are various ways to do this - here's one...
Start by calculating the translation values and the scale values, the concatenate them:
let translation = CGAffineTransform(translationX: xMove, y: yMove)
let scaling = CGAffineTransform(scaleX: xScale, y: yScale)
let fullTransform = scaling.concatenating(translation)
Here's a complete example... we add two labels with different font sizes, locations and background colors (to make it easy to see). Tap anywhere to run the transform animation:
class ViewController: UIViewController {
let labelA = UILabel()
let labelB = UILabel()
var aTop: NSLayoutConstraint!
var aLeading: NSLayoutConstraint!
var bTop: NSLayoutConstraint!
var bLeading: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
[labelA, labelB].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
labelA.text = "Label A"
labelB.text = "Label B"
labelA.backgroundColor = .green
labelB.backgroundColor = .cyan
labelA.font = .systemFont(ofSize: 40.0)
labelB.font = .systemFont(ofSize: 20.0)
// respect safe area
let g = view.safeAreaLayoutGuide
aTop = labelA.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0)
aLeading = labelA.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0)
bTop = labelB.topAnchor.constraint(equalTo: g.topAnchor, constant: 300.0)
bLeading = labelB.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 240.0)
NSLayoutConstraint.activate([
aTop, aLeading,
bTop, bLeading,
])
let t = UITapGestureRecognizer(target: self, action: #selector(self.doAnim(_:)))
view.addGestureRecognizer(t)
}
@objc func doAnim(_ g: UITapGestureRecognizer?) -> Void {
let targetPoint = labelA.center
let originPoint = labelB.center
let xMove = targetPoint.x - originPoint.x
let yMove = targetPoint.y - originPoint.y
let xScale = labelA.frame.width / labelB.frame.width
let yScale = labelA.frame.height / labelB.frame.height
let translation = CGAffineTransform(translationX: xMove, y: yMove)
let scaling = CGAffineTransform(scaleX: xScale, y: yScale)
let fullTransform = scaling.concatenating(translation)
UIView.animate(withDuration: 1.0, animations: {
self.labelB.transform = fullTransform
}) { [weak self] b in
guard let self = self else { return }
self.labelB.transform = .identity
self.labelB.font = self.labelA.font
self.bTop.constant = self.aTop.constant
self.bLeading.constant = self.aLeading.constant
}
}
}