gpt4 book ai didi

ios - 为什么约束更改或动画不需要调用setNeedsUpdateConstraints?

转载 作者:IT王子 更新时间:2023-10-29 05:25:04 28 4
gpt4 key购买 nike

读物:

从这个answer:

这是被接受的答案建议为您的 View 动画设置动画:

_addBannerDistanceFromBottomConstraint.constant = 0

UIView.animate(withDuration: 5) {
self.view.layoutIfNeeded()
}

不是更改框架时,为什么调用 layoutIfNeeded。我们正在更改约束,因此(根据此 other answer)我们不应该而是调用 setNeedsUpdateConstraints吗?

同样,此备受关注的 answer说:

If something changes later on that invalidates one of your constraints, you should remove the constraint immediately and call setNeedsUpdateConstraints



观察结果:

我实际上确实尝试过同时使用它们。
使用 setNeedsLayout,我的 View 正确设置了左侧的动画
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 并不会生成动画,它只是将 View 快速移至左侧
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.setNeedsLayoutview.setNeedsUpdateConstraints将其向左移动。然而:

带有 view.setNeedsLayout
  • ,点击我的按钮后,到达我的viewDidLayoutSubviews断点。但是,从未达到updateViewConstraints断点。这使我对如何更新约束感到困惑...
  • 使用 view.setNeedsUpdateConstraints
  • ,点击按钮后,到达我的updateViewConstraints断点,然后到达viewDidLayoutSubviews断点。这确实是有道理的,更新了约束,然后调用layoutSubviews。

  • 问题:

    根据我的读物:如果更改约束,然后使其生效,则必须调用 setNeedsUpdateConstraints,但是基于我的观察,这是错误的。拥有以下代码足以制作动画:
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()

    为什么?

    然后,我想也许是通过某种方式通过其他方式更新了约束。因此,我在 override func updateViewConstraintsoverride func viewDidLayoutSubviews处设置了一个断点,但是只有 viewDidLayoutSubviews达到了它的断点。

    那么,自动版式引擎如何管理呢?

    最佳答案

    这是iOS开发人员中常见的误解。

    这是我的“自动版式”的“黄金法则”之一:

    不要为“更新约束”而烦恼。

    您无需调用以下任何一种方法:

  • setNeedsUpdateConstraints()
  • updateConstraintsIfNeeded()
  • updateConstraints()
  • updateViewConstraints()

  • 除了极少数情况下,您的布局非常复杂会减慢您的应用程序运行速度(或者您故意选择以非典型方式实现布局更改)。

    更改布局的首选方法

    通常,当您要更改布局时,您可以在点击按钮或触发更改的任何事件后立即激活/停用或更改布局约束,例如,在按钮的操作方法中:
    @IBAction func toggleLayoutButtonTapped(_ button: UIButton) {
    toggleLayout()
    }

    func toggleLayout() {
    isCenteredLayout = !isCenteredLayout

    if isCenteredLayout {
    centerXConstraint.isActive = true
    } else {
    centerXConstraint.isActive = false
    }
    }

    正如苹果在其 Auto Layout Guide中所说的那样:

    It is almost always cleaner and easier to update a constraint immediately after the affecting change has occurred. Deferring these changes to a later method makes the code more complex and harder to understand.



    当然,您也可以在动画中包装此约束更改:首先执行约束更改,然后通过在动画闭包中调用 layoutIfNeeded()为更改设置动画:
    @IBAction func toggleLayoutButtonTapped(_ button: UIButton) {
    // 1. Perform constraint changes:
    toggleLayout()
    // 2. Animate the changes:
    UIView.animate(withDuration: 1.8, animations: {
    view.layoutIfNeeded()
    }
    }

    每当您更改约束时,系统都会自动安排延迟的布局遍历,这意味着系统将在不久的将来重新计算布局。无需调用 setNeedsUpdateConstraints(),因为您只是自己更新(更改)了约束!需要更新的是布局,即所有 View 的框架,而不是其他任何约束。

    无效原则

    如前所述,iOS布局系统通常不会立即对约束更改使用react,而只会安排延迟的布局通过。这是出于性能原因。这样想:

    When you go shopping groceries, you put an item in your cart but you don't pay it immediately. Instead, you put other items in your cart until you feel like you got everything you need. Only then you proceed to the cashier and pay all your groceries at once. It's way more efficient.



    由于这种延迟的布局传递,因此需要一种特殊的机制来处理布局更改。我称之为无效原则。这是一个两步机制:
  • 您将某些内容标记为无效。
  • 如果无效,请执行一些操作以使其再次有效。

  • 就布局引擎而言,它对应于:
  • setNeedsLayout()
  • layoutIfNeeded()


  • setNeedsUpdateConstraints()
  • updateConstraintsIfNeeded()

  • 第一对方法将导致立即(不延迟)布局通过:首先使布局无效,然后在布局无效时立即重新计算布局(当然是这样)。

    通常,您不介意现在或几毫秒后再进行布局传递,因此通常只调用 setNeedsLayout()来使布局无效,然后等待延迟的布局传递。这使您有机会对约束进行其他更改,然后稍晚但一次全部更新布局(→购物车)。

    仅当您需要立即重新计算布局时,才需要调用 layoutIfNeeded()。当您需要根据新布局的结果框架执行其他一些计算时,可能就是这种情况。

    第二对方法将导致立即调用 updateConstraints()(在 View 上或在 View Controller 上的 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()

    我希望这可以使事情变得更清晰。

    其他资源:
  • Auto Layout – From Leading to Trailing:我在UIKonf 2017上的演讲,主题:
  • "The Layout Pass"
  • "Updating Constraints"
  • The Auto Layout Comprehendium™:向下滚动到由我维护的“更新约束”部分
  • Apple的Auto Layout Guide:部分
  • “更改约束”
  • “延迟的布局通行证”
  • 关于ios - 为什么约束更改或动画不需要调用setNeedsUpdateConstraints?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47823639/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com