You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:27:17