为何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
相关产品推荐
相关产品推荐

