NSLayoutConstraint动画异常:UILabel宽度设为30时无动画效果
问题:UILabel宽度从300变回30时无动画效果?
这段代码里点击视图切换lblA的宽度,从30变到300时有动画,但从300变回30时没有动画,这是为什么?
import UIKit class ViewController: UIViewController { let lblA = UILabel() var lblAWidthConstraint: NSLayoutConstraint? var tag = false // let lblB = UILabel() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.lblA.backgroundColor = .red self.view.addSubview(self.lblA) self.lblA.translatesAutoresizingMaskIntoConstraints = false self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[v]" , options: [] , metrics: nil , views: ["v" : self.lblA])) self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v]|" , options: [] , metrics: nil , views: ["v" : self.lblA])) let widthConstraint = self.lblA.widthAnchor.constraint(equalToConstant: 30) widthConstraint.isActive = true self.lblAWidthConstraint = widthConstraint // let tap = UITapGestureRecognizer(target: self, action: #selector(tapView)) self.view.addGestureRecognizer(tap) } @objc func tapView() { tag = !tag self.lblAWidthConstraint?.constant = tag ? 300 : 30 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } } }
嗨,我来帮你搞清楚这个问题哈~
问题根源
你遇到的这个动画不一致的情况,核心原因出在Auto Layout的布局时机和约束组合上:
- 你给
lblA设置了水平方向的|[v]约束(左边缘贴紧父视图),再加上一个可变的宽度约束。 - 当你把宽度从300改回30时,这个值和
viewDidLoad里初始化的宽度约束值完全相同。系统会认为这是“恢复到初始布局状态”,在动画块执行之前就提前完成了布局调整,导致动画块里的layoutIfNeeded()实际上没有触发任何可动画的布局变化,所以看不到动画。 - 而从30变到300时,是全新的约束值,系统需要重新计算布局,动画块能正常捕获到这个变化并执行动画。
解决方案
只需要在修改约束值之后,先告诉系统“布局需要更新”,再执行动画即可:
修改你的tapView方法:
@objc func tapView() { tag = !tag self.lblAWidthConstraint?.constant = tag ? 300 : 30 // 先标记需要更新布局 self.view.setNeedsLayout() UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } }
另外,顺便提一句,你代码里创建tap手势的那行被注释掉了,直接addGestureRecognizer(tap)会导致崩溃,记得把注释去掉:
let tap = UITapGestureRecognizer(target: self, action: #selector(tapView)) self.view.addGestureRecognizer(tap)
这样修改后,无论是从30→300还是300→30,动画都会正常执行啦~
内容的提问来源于stack exchange,提问作者ZYiOS
相关产品推荐
相关产品推荐

