从这个答案:
这是被接受的答案建议为您的视图变化设置动画:
_addBannerDistanceFromBottomConstraint.constant = 0 UIView.animate(withDuration: 5) { self.view.layoutIfNeeded() }
不 更改框架layoutIfNeeded时为什么要打电话。我们正在更改约束,所以(根据[另一个答案]我们不应该打电话吗? setNeedsUpdateConstraints
layoutIfNeeded
setNeedsUpdateConstraints
如果以后发生的更改使您的约束之一无效,则应立即删除约束并调用setNeedsUpdateConstraints
我实际上确实尝试过同时使用它们。使用setNeedsLayout我的视图 可以向右正确设置动画
setNeedsLayout
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func animate(_ sender: UIButton) { UIView.animate(withDuration: 1.8, animations: { self.centerXConstraint.isActive = !self.centerXConstraint.isActive self.view.setNeedsLayout() self.view.layoutIfNeeded() }) } @IBOutlet weak var centerYConstraint: NSLayoutConstraint! @IBOutlet var centerXConstraint: NSLayoutConstraint! }
但是,使用setNeedsUpdateConstraints 不会 设置动画,它只是将视图 快速向左 移动。
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func animate(_ sender: UIButton) { UIView.animate(withDuration: 1.8, animations: { self.centerXConstraint.isActive = !self.centerXConstraint.isActive self.view.setNeedsUpdateConstraints() self.view.updateConstraintsIfNeeded() }) } @IBOutlet weak var centerYConstraint: NSLayoutConstraint! @IBOutlet var centerXConstraint: NSLayoutConstraint! }
如果我不想动画,请使用其中之一view.setNeedsLayout或view.setNeedsUpdateConstraints将其向左移动。然而:
view.setNeedsLayout
view.setNeedsUpdateConstraints
viewDidLayoutSubviews
updateViewConstraints
根据我的读物:如果您更改约束条件,然后使其生效,则必须致电setNeedsUpdateConstraints,但是根据我的观察,这是错误的。拥有以下代码足以制作动画:
self.view.setNeedsLayout() self.view.layoutIfNeeded()
为什么?
然后我想也许是在幕后通过其他方式更新了约束。所以,我把一个断点override func updateViewConstraints和overridefuncviewDidLayoutSubviews,但只viewDidLayoutSubviews达到了断点。
override func updateViewConstraints
overridefuncviewDidLayoutSubviews
那么,自动版式引擎如何管理呢?
这是iOS开发人员中常见的误解。
这是我的“自动版式”的“黄金法则”之一:
你 永远 需要调用任何这些方法:
setNeedsUpdateConstraints()
updateConstraintsIfNeeded()
updateConstraints()
updateViewConstraints()
除了 极少数情况下,您的布局非常复杂会减慢您的应用程序运行速度(或者您故意选择以非典型方式实施布局更改)。
通常,当您想更改布局时,您可以在点击按钮或发生任何事件触发更改后立即激活/停用或更改布局约束,例如,在按钮的操作方法中:
@IBAction func toggleLayoutButtonTapped(_ button: UIButton) { toggleLayout() } func toggleLayout() { isCenteredLayout = !isCenteredLayout if isCenteredLayout { centerXConstraint.isActive = true } else { centerXConstraint.isActive = false } }
正如Apple在其《自动布局指南》中所说:
发生影响性更改之后,几乎总是更干净,更轻松地立即更新约束。将这些更改推迟到以后的方法中会使代码更加复杂且难以理解。
当然,您也可以在动画中包装此约束更改:首先执行约束更改,然后通过调用layoutIfNeeded()动画闭包为更改设置动画:
layoutIfNeeded()
@IBAction func toggleLayoutButtonTapped(_ button: UIButton) { // 1. Perform constraint changes: toggleLayout() // 2. Animate the changes: UIView.animate(withDuration: 1.8, animations: { view.layoutIfNeeded() } }
每当您更改约束时,系统都会 自动 安排延迟的布局遍历,这意味着系统将在不久的将来重新计算布局。无需致电,setNeedsUpdateConstraints()因为您只是 自己 更新(更改)约束!需要更新的是布局,即所有视图的框架, 而不是 其他任何约束。
如前所述,iOS布局系统通常不会立即对约束更改做出反应,而只会安排延迟的布局通过。这是出于性能原因。这样想:
当您去购物时,您将商品放入购物车,但没有立即付款。取而代之的是,您将其他物品放到购物车中,直到感觉得到所需的一切为止。只有这样,您才能前往收银员并立即支付所有杂货。这样更有效。
由于这种延迟的布局传递,需要一种特殊的机制来处理布局更改。我称之为 无效原则 。这是一个两步机制:
就布局引擎而言,它对应于:
setNeedsLayout()
和
第一对方法 将导致 立即 (而不是延迟)布局通过:首先使布局无效,然后在布局无效时立即重新计算布局(当然是这样)。
通常您不介意现在或几毫秒后进行布局传递,因此通常只调用setNeedsLayout()使布局无效,然后等待推迟的布局传递。这使您有机会对约束进行其他更改,然后稍晚但一次全部更新布局(→购物车)。
你只需要调用layoutIfNeeded(),当你需要的布局被重新计算 现在 。当您需要根据新布局的结果框架执行其他一些计算时,可能就是这种情况。
第二对方法 将导致立即调用updateConstraints()(在视图上或updateViewConstraints()在视图控制器上)。但这是您通常不应该执行的操作。
仅当布局 真的很 慢并且UI因布局更改而感到迟钝时,您才可以选择与上述方法不同的方法:您不必对按钮的点击直接更新约束,而只是在“注释”一下什么您要更改,另一个“说明”是您的约束需要更新。
@IBAction func toggleLayoutButtonTapped(_ button: UIButton) { // 1. Make a note how you want your layout to change: isCenteredLayout = !isCenteredLayout // 2. Make a note that your constraints need to be updated (invalidate constraints): setNeedsUpdateConstraints() }
这将安排一个延迟的布局过程,并确保在布局过程中将调用updateConstraints()/ updateViewConstraints()。因此,您现在甚至可以执行其他更改并调用setNeedsUpdateConstraints()一千次- 您的约束在下一次布局遍历中仍只会更新 一次 。
现在,您覆盖updateConstraints()/updateViewConstraints()并根据您当前的布局状态(即您在上面的“1”中“注意到”的内容)执行必要的约束更改:
override func updateConstraints() { if isCenteredLayout { centerXConstraint.isActive = true } else { centerXConstraint.isActive = false } super.updateConstraints() }
同样,如果布局真的很慢并且您要处理数百或数千个约束,那么这只是您的最后选择。我 从未updateConstraints()在任何项目中使用过。
我希望这可以使事情变得更清晰。