Search code examples
iosswiftuilabeluiviewanimation

UIView animating to wrong position after being scaled while using constraints


So I'm trying to animate an UILabel to match another UILabel size and position. At first I was only working with the animation of the position part using constraints like this:

private lazy var constraintsForStateA: [NSLayoutConstraint] = [
    firstLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
    firstLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor)
]


private lazy var constraintsForStateB: [NSLayoutConstraint] = [
    firstLabel.leadingAnchor.constraint(equalTo: secondLabel.leadingAnchor),
    firstLabel.topAnchor.constraint(equalTo: secondLabel.topAnchor)
]

So I basically have two arrays with these constraints above (constraintsForStateA and constraintsForStateB), and when I need to animate the position of my firstLabel I just do something like:

// From state A to state B
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
        NSLayoutConstraint.deactivate(self._constraintsUnselected)
        NSLayoutConstraint.activate(self._constraintsSelected)
        self.layoutIfNeeded()
    } completion: { _ in
        self.firstLabel.alpha = 0
        self.secondLabel.alpha = 1
    }
}

So far this has been working exactly as I was expecting, but the part with the size is giving me some trouble, since I can't apply the same strategy to the text size I'm using a transform like this:

let scaleX = secondLabel.bounds.width / firstLabel.bounds.width
let scaleY = secondLabel.bounds.height / firstLabel.bounds.height

and then in the same .animate method above from state A to state B I do this:

firstLabel.transform = CGAffineTransform(scaleX: scaleX, y: scaleY)

and from state B to state A:

firstLabel.transform = .identity

Which works as I want but the problem I'm facing is that the position is no longer in the expected place. I think this is happening because the transformation is happening having in consideration the anchorPoint at the center of the label. I've tried sort of blindly making it work changing the anchorPoint to be at (0,0), but it's not working either. I've lost 2 days on this already, any help or guidance is welcome!


Solution

  • When you animate, it would be much simpler if you just forget about the constraints and just deal with frames:

    NSLayoutConstraint.deactivate(self.constraintsForStateA)
    firstLabel.translatesAutoresizingMaskIntoConstraints = true
    UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
        firstLabel.frame = secondLabel.frame
    }
    

    Then to transition from state B to A:

    firstLabel.translatesAutoresizingMaskIntoConstraints = false
    UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
        NSLayoutConstraint.activate(self.constraintsForStateA)
        self.layoutIfNeeded()
    }