Post

Replies

Boosts

Views

Activity

UILabel constraint animation bug
When animating a change in width or height constraint .constant on a UILabel, the animation only works if the constant value is larger. If the value is smaller, the label "snaps" to its new size instead of animating. Quick, clear example... two views (green) and two labels (cyan). Tapping anywhere will toggle the respective height / width constraint constants between 300 and 100. The green views animate smoothly whether "shrinking" or "growing." The cyan labels only animate when "growing" ... when "shrinking" they "snap" to the new constant value: import UIKit class ViewController: UIViewController { let testHeightLabel: UILabel = { let v = UILabel() v.text = "Height" v.backgroundColor = .cyan return v }() let testHeightView: UIView = { let v = UIView() v.backgroundColor = .green return v }() let testWidthLabel: UILabel = { let v = UILabel() v.text = "Width" v.backgroundColor = .cyan return v }() let testWidthView: UIView = { let v = UIView() v.backgroundColor = .green return v }() var testViewHeightConstraint: NSLayoutConstraint! var testLabelHeightConstraint: NSLayoutConstraint! var testViewWidthConstraint: NSLayoutConstraint! var testLabelWidthConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() [testHeightView, testHeightLabel, testWidthView, testWidthLabel].forEach { v in v.translatesAutoresizingMaskIntoConstraints = false view.addSubview(v) } testViewHeightConstraint = testHeightView.heightAnchor.constraint(equalToConstant: 300.0) testLabelHeightConstraint = testHeightLabel.heightAnchor.constraint(equalToConstant: 300.0) testViewWidthConstraint = testWidthView.widthAnchor.constraint(equalToConstant: 300.0) testLabelWidthConstraint = testWidthLabel.widthAnchor.constraint(equalToConstant: 300.0) let g = view.safeAreaLayoutGuide NSLayoutConstraint.activate([ // constrain testHeightView Top / Leading / Width testHeightView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0), testHeightView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 80.0), testHeightView.widthAnchor.constraint(equalToConstant: 60.0), // constrain testHeightLabel Top / Leading / Width testHeightLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0), testHeightLabel.leadingAnchor.constraint(equalTo: testHeightView.trailingAnchor, constant: 40.0), testHeightLabel.widthAnchor.constraint(equalToConstant: 60.0), // constrain testWidthView Top / Leading / Height testWidthView.topAnchor.constraint(equalTo: g.topAnchor, constant: 360.0), testWidthView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0), testWidthView.heightAnchor.constraint(equalToConstant: 40.0), // constrain testWidthLabel Top / Leading / Height testWidthLabel.topAnchor.constraint(equalTo: testWidthView.bottomAnchor, constant: 20.0), testWidthLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0), testWidthLabel.heightAnchor.constraint(equalToConstant: 40.0), // activate height and width constraints testViewHeightConstraint, testLabelHeightConstraint, testViewWidthConstraint, testLabelWidthConstraint, ]) let instructionLabel = UILabel() instructionLabel.text = "Tap to toggle height and width constraint constants.\nGreen Views smoothly animate to the new height and width.\nCyan Labels only smoothly animate when \"growing\"." instructionLabel.numberOfLines = 0 instructionLabel.textAlignment = .center instructionLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(instructionLabel) NSLayoutConstraint.activate([ instructionLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0), instructionLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0), instructionLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0), ]) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // toggle height constraint constants between 300 and 100 testViewHeightConstraint.constant = testViewHeightConstraint.constant == 300.0 ? 100.0 : 300.0 testLabelHeightConstraint.constant = testLabelHeightConstraint.constant == 300.0 ? 100.0 : 300.0 // toggle width constraint constants between 300 and 100 testViewWidthConstraint.constant = testViewWidthConstraint.constant == 300.0 ? 100.0 : 300.0 testLabelWidthConstraint.constant = testLabelWidthConstraint.constant == 300.0 ? 100.0 : 300.0 // animate the constraint change UIView.animate(withDuration: 1.5, animations: { self.view.layoutIfNeeded() }) } } It's possible I'm missing something obvious? Note that the "snap" does not occur if the label is embedded in a container and the constraint on the container is animated - but that would be an undesirable workaround. So, just looking for confirmation of this being a "bug."
1
0
995
Feb ’23