Hon*_*ney 13 animation ios autolayout nslayoutconstraint swift
从这个答案:
这是接受的答案建议为视图更改设置动画:
_addBannerDistanceFromBottomConstraint.constant = 0
UIView.animate(withDuration: 5) {
self.view.layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)
layoutIfNeeded当我们不更改帧时,为什么要打电话.我们正在改变约束,所以(根据这个其他答案)我们不应该调用setNeedsUpdateConstraints吗?
同样,这个备受好评的答案说:
如果稍后发生某些变化会使您的某个约束无效,则应立即删除约束并调用setNeedsUpdateConstraints
我确实尝试过使用它们.使用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!
}
Run Code Online (Sandbox Code Playgroud)
但是使用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!
}
Run Code Online (Sandbox Code Playgroud)
如果我不想要动画,那么使用其中任何一个view.setNeedsLayout或view.setNeedsUpdateConstraints向左移动它.然而:
view.setNeedsLayout,我的按钮被窃听后,我viewDidLayoutSubviews到达断点.但是updateViewConstraints从未达到断点.这让我感到困惑的是如何更新约束......view.setNeedsUpdateConstraints,点击按钮后,我的 updateViewConstraints断点到达,然后到达viewDidLayoutSubviews断点.这确实有意义,更新约束,然后调用layoutSubviews.根据我的读数:如果你改变约束然后让它变得有效你必须打电话setNeedsUpdateConstraints,但根据我的观察,这是错误的.拥有以下代码足以动画:
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
Run Code Online (Sandbox Code Playgroud)
为什么?
然后我想也许不知何故,它通过其他方式更新约束.所以,我把一个断点override func updateViewConstraints和override func viewDidLayoutSubviews,但只viewDidLayoutSubviews达到了断点.
那么Auto Layout引擎如何管理呢?
Mis*_*cha 39
这是iOS开发人员常见的误解.
这是我自动布局的"黄金规则"之一:
您永远不需要调用以下任何方法:
setNeedsUpdateConstraints()updateConstraintsIfNeeded()updateConstraints()updateViewConstraints()除了非常罕见的情况,你有一个非常复杂的布局,这会减慢你的应用程序(或者你故意选择以非典型的方式实现布局更改).
通常,当您想要更改布局时,您可以在按钮点击或触发更改的任何事件后直接激活/停用或更改布局约束,例如在按钮的操作方法中:
@IBAction func toggleLayoutButtonTapped(_ button: UIButton) {
toggleLayout()
}
func toggleLayout() {
isCenteredLayout = !isCenteredLayout
if isCenteredLayout {
centerXConstraint.isActive = true
} else {
centerXConstraint.isActive = false
}
}
Run Code Online (Sandbox Code Playgroud)
正如Apple在其自动布局指南中所说:
在发生影响更改后立即更新约束几乎总是更清晰,更容易.将这些更改推迟到以后的方法会使代码更复杂,更难理解.
您当然也可以在动画中包含此约束更改:首先执行约束更改,然后通过调用layoutIfNeeded()动画闭包来设置更改的动画:
@IBAction func toggleLayoutButtonTapped(_ button: UIButton) {
// 1. Perform constraint changes:
toggleLayout()
// 2. Animate the changes:
UIView.animate(withDuration: 1.8, animations: {
view.layoutIfNeeded()
}
}
Run Code Online (Sandbox Code Playgroud)
无论何时更改约束,系统都会自动计划延迟布局传递,这意味着系统将在不久的将来重新计算布局.无需打电话,setNeedsUpdateConstraints()因为您刚刚自己更新(更改)约束!需要更新的是布局,即所有视图的框架,而不是任何其他约束.
如前所述,iOS布局系统通常不会立即对约束更改做出反应,而只调度延迟布局传递.这是出于性能原因.可以这样想:
当您购买杂货时,您将物品放入购物车但不立即支付.相反,您将其他物品放入购物车,直到您觉得自己拥有所需的一切.只有这样,您才能前往收银台并立即支付所有杂货.它的效率更高.
由于这种延迟布局传递,需要一种特殊的机制来处理布局更改.我称之为无效原则.这是一个两步机制:
就布局引擎而言,这对应于:
setNeedsLayout()layoutIfNeeded()和
setNeedsUpdateConstraints()updateConstraintsIfNeeded()第一对方法将导致立即(非延迟)布局传递:首先使布局无效,然后如果布局无效(当然是布局),则立即重新计算布局.
如果布局传递将在现在或几毫秒之后发生,通常您不会打扰,因此通常只调用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()
}
Run Code Online (Sandbox Code Playgroud)
这会调度延迟布局传递并确保在布局传递期间调用updateConstraints()/ updateViewConstraints().因此,您现在甚至可以执行其他更改并调用setNeedsUpdateConstraints()一千次 - 您的约束仍将仅在下一个布局过程中更新一次.
现在你覆盖updateConstraints()/ updateViewConstraints()并根据你当前的布局状态执行必要的约束更改(即你在"1."中有"注意到"的内容):
override func updateConstraints() {
if isCenteredLayout {
centerXConstraint.isActive = true
} else {
centerXConstraint.isActive = false
}
super.updateConstraints()
}
Run Code Online (Sandbox Code Playgroud)
同样,如果布局非常慢并且您要处理数百或数千个约束,这只是您的最后手段.我从未需要updateConstraints()在任何项目中使用过.
我希望这会让事情变得更加清晰.
| 归档时间: |
|
| 查看次数: |
3162 次 |
| 最近记录: |