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

为何layoutIfNeeded()无法为NavigationBar的BarButtonItem添加动画?

问题描述

我正在开发一款简单的电商App,通过动态BarButtonItem展示用户购物车的总价,希望为该按钮添加总价变化时的伸缩动画。我在更新按钮约束后使用了layoutIfNeeded()方法,但动画并未生效。

效果动图
购物车按钮动画效果

相关代码

class ProductsViewController: UIViewController {

  ...
  var cartBarButton: UIBarButtonItem!
  var cartBarButtonWidthConstraint: NSLayoutConstraint?
  var price: Double = 0
  ...

  override func viewDidLoad() {
    super.viewDidLoad()

    configureCartButton()
    ...
  }

  func configureCartButton() {

    let cartButton = CustomCartButton(color: color, image: image, title: price.currencyString)

    cartButton.addTarget(self, action: #selector(updateCartCost), for: .touchUpInside)
        
    cartBarButton = UIBarButtonItem(customView: cartButton)
    cartBarButton.customView?.translatesAutoresizingMaskIntoConstraints = false
        
    cartBarButtonWidthConstraint = cartBarButton.expand(considering: price) // expand()方法是UIBarButtonItem的扩展,返回按钮宽度约束
    cartBarButtonWidthConstraint?.isActive = true
        
    navigationItem.rightBarButtonItem = cartBarButton

  }

  @objc private func updateCartCost() {
    
    cartBarButtonWidthConstraint?.isActive = false
    cartBarButtonWidthConstraint = cartBarButton.collapse(to: 25) // collapse()方法是UIBarButtonItem的扩展
    cartBarButtonWidthConstraint?.isActive = true
    UIView.animate(withDuration: 1) {
      self.view.layoutIfNeeded()
    }

    price += 90
    cartBarButtonWidthConstraint?.isActive = false
    cartBarButtonWidthConstraint = cartBarButton.expand(considering: price)
    cartBarButtonWidthConstraint?.isActive = true
    UIView.animate(withDuration: 1) {
      self.view.layoutIfNeeded()
    }
    self.cartButton.title = self.price.currencyString
  }

}
解决方案

你的代码存在两个关键问题导致动画失效:

  • 动画未按顺序执行:连续调用两次动画时,后一次的约束修改会立即覆盖前一次,导致两次动画都无法正常触发。必须通过动画的completion闭包来串联收缩和展开的流程。
  • 布局刷新时机错误:修改约束后,需要先在动画闭包外调用layoutIfNeeded()确认当前布局状态,再在闭包内调用该方法触发动画。

修改后的updateCartCost方法如下:

@objc private func updateCartCost() {
    // 执行收缩动画
    cartBarButtonWidthConstraint?.isActive = false
    cartBarButtonWidthConstraint = cartBarButton.collapse(to: 25)
    cartBarButtonWidthConstraint?.isActive = true
    
    // 刷新当前布局,为动画做准备
    view.layoutIfNeeded()
    
    UIView.animate(withDuration: 1, animations: {
        self.view.layoutIfNeeded()
    }, completion: { _ in
        // 收缩完成后更新价格并执行展开动画
        self.price += 90
        
        // 更新宽度约束
        self.cartBarButtonWidthConstraint?.isActive = false
        self.cartBarButtonWidthConstraint = self.cartBarButton.expand(considering: self.price)
        self.cartBarButtonWidthConstraint?.isActive = true
        
        // 更新按钮标题(注意通过customView强转获取实例)
        if let customBtn = self.cartBarButton.customView as? CustomCartButton {
            customBtn.title = self.price.currencyString
        }
        
        // 刷新布局,准备展开动画
        self.view.layoutIfNeeded()
        
        UIView.animate(withDuration: 1) {
            self.view.layoutIfNeeded()
        }
    })
}

额外注意事项:

  • 确保CustomCartButton内部的标题、图片等元素是通过Auto Layout约束布局的,这样按钮宽度变化时内部元素才能跟随动画调整。
  • 不要直接使用未定义的self.cartButton,必须通过cartBarButton.customView强转为CustomCartButton后再修改标题,避免变量未定义错误。

内容的提问来源于stack exchange,提问作者altunog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 01:00:57