【问题标题】:What is _UITemporaryLayoutWidth and why does it break my constraints?什么是 _UITemporaryLayoutWidth,为什么它会打破我的约束?
【发布时间】:2015-09-07 06:41:48
【问题描述】:

我有一个带有 Autolayout 和 Size Classes 的故事板。相当复杂的布局,不幸的是我无法确定如何在新项目中重现该问题。
但是有问题的视图被固定到屏幕的左右边缘,具有 750 优先级的约束(即|-(0@750)-[myView]-(0@750)-|,此外它具有大于或等于优先级为 1000 的约束(即|-(>=0)-[myView]-(>=0)-|)。这样做是为了限制iPad 上的宽度,因此有width <= 600 @1000 的宽度约束,以及容器中水平中心 约束。最重要的是视图的纵横比约束为 3:1。正如我所说,相当复杂。

Interface Builder 没有显示任何约束问题。 Xcode 布局预览在所有设备上都正确呈现。

当我运行应用程序时,iOS 告诉我它有冲突的约束。

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fd72853ff80 DMXKit.DipSwitchAssembly:0x7fd72990d0e0.width == 3*DMXKit.DipSwitchAssembly:0x7fd72990d0e0.height>",
    "<NSLayoutConstraint:0x7fd728574e50 '_UITemporaryLayoutWidth' H:[DMXKit.DipSwitchAssembly:0x7fd72990d0e0(400)]>",
    "<NSLayoutConstraint:0x7fd72856e9c0 V:[DMXKit.DipSwitchAssembly:0x7fd72990d0e0(133)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7ffb19798000 DMXKit.DipSwitchAssembly:0x7ffb1979a000.width == 3*DMXKit.DipSwitchAssembly:0x7ffb1979a000.height>

这会重复几次,具有完全相同(即相同的指针)约束。这也很奇怪,看起来约束并没有真正被打破。运行时,应用程序看起来 100% 正确。当我在 Xcode 视图调试器中查看应用程序时,地址为 0x7ffb19798000 的约束仍然存在,因此它从未被破坏。

_UITemporaryLayoutWidth 约束从何而来?显然我没有添加它。谷歌没有吐出任何关于_UITemporaryLayoutWidth 的有用信息。有人遇到过这样的问题吗?

【问题讨论】:

  • 我在日志中也有很多这样的警告。虽然,它似乎并没有打破我的布局。我想知道这些_UITemporaryLayoutWidths 是从哪里来的。

标签: ios autolayout nslayoutconstraint


【解决方案1】:

所以,我不确定这是否可以帮助您解决问题,因为听起来您正在 IB 中构建布局,但这是我刚刚遇到的一个问题,可能会帮助其他谷歌搜索“_UITemporaryLayoutWidth”的人.

我的情况是我在初始化期间添加了自动布局约束,并且在将视图添加到视图层次结构之前意外触发了“layoutIfNeeded”(修改按钮边缘插入触发布局)。看起来系统添加了这些临时约束并且(我假设?)在视图添加到层​​次结构后将它们删除。临时约束设置了 0 个维度(在我的情况下),例如,如果您在布局中设置特定的约束常量值(例如,您希望这些按钮之间有 16px 但该维度中有 0 个空间...),则可能会失败。

更新: 调查了一下,发现临时约束似乎和你用来初始化父视图的框架对应。在我的情况下,我使用的框架大小为 0x0,因此临时约束相对于 0x0 大小进行评估,因此失败。已验证如果我使用 5x5 帧进行初始化,那么我会看到与 _UITemporaryLayoutWidth(5) 约束相同的错误。我注意到的另一件事是在布局评估期间似乎添加和删除了约束 - 如果我在触发布局并分析约束之前中断,那么我看不到临时约束。我也没有看到错误后的约束,所以我怀疑他们综合了临时约束,添加它们,解决然后删除它们。

更新 2: 好的,也许这是 TMI,但这可能对某些人有用。我目前的想法是,这些临时约束是对系统如何在单个视图层次结构中处理混合帧操作和自动布局的一瞥。就我而言,我的视图没有父视图,也没有定义其大小的约束,但它确实有一个零帧,这意味着系统假定这是它在解决布局时应该使用的大小。我的想法是,系统为明确设置了框架的视图综合了这些临时约束,以在遍历层次结构时解决系统的其余部分。

更新 3: 所以,我又遇到了这个问题,想分享更多信息。我的场景是,我试图使用 systemLayoutSizeFittingSize: 方法从本质上测量没有父视图的视图。 长话短说,我必须在测量视图之前调用 layoutIfNeeded - 这是我遇到临时约束冲突的地方,因为视图没有超级视图。临时约束常量 (尺寸)确实对应于没有超级视图的视图上设置的任何框架 - 我认为这是有道理的(虽然,在您将要与 AutoLayout 一起使用的视图上设置一个框架似乎很奇怪......)我能够通过说来解决临时约束问题 - 如果视图没有超级视图,则将其添加到临时超级视图;措施;从临时超级视图中删除它。希望这有用。

【讨论】:

  • ..."在将视图添加到视图层次结构之前意外触发了 'layoutIfNeeded'" -- 谢谢。
  • 旅行者,您所寻求的答案是阿达玛在上面的评论中总结的。
  • 当视图还没有超级视图但子视图将使用 AutoLayout 进行复杂布局时发生在我身上。
  • 我花了 4 个小时调试约束,直到我看到 @Adama 的评论。
【解决方案2】:

如上所述,临时约束似乎是在没有父视图的视图上设置的。我发现在顶层视图上确保translatesAutoresizingMaskIntoConstraintsfalse 消除了临时约束。如:

topView.translatesAutoresizingMaskIntoConstraints = false

【讨论】:

  • 我不需要在超级视图上设置这个标志 - 只需添加它就足够了。这是在单元测试中。
【解决方案3】:

我的情况是,问题如下:

我创建了一个UIButton
我设置了它的宽度和高度限制。
我将按钮添加到它的超级视图中。
我将其前导和顶部布局约束设置为超级视图。

这产生了错误“Unable to simultaneously satisfy constraints”,其中一个冲突的约束是_UITemporaryLayoutHeight

当我将按钮添加到其父视图设置其宽度和高度约束之前,错误消失了。

【讨论】:

    【解决方案4】:

    在我的情况下,我只在这种情况下遇到了这个 UITemporaryLayoutWidth 冲突

    1. 我用 [UIView new] 创建了一个视图,但我没有将它添加到任何 超级视图
    2. 然后我为刚刚创建的这个视图创建一个宽度约束
    3. 立即调用layoutIfNeeded到这个视图,和冲突 发生

    所以消除这个错误的方法很简单:在添加到某些超级视图之前不要自己调用 layoutIfNeeded

    【讨论】:

      【解决方案5】:

      当我使用视图的布局边距锚点添加约束时,我遇到了这个问题。当我将约束从布局边距锚更改为视图的顶部、底部、前导、尾随锚时,错误消失了。

      我正在使用.zero rect 创建我的超级视图。

      但由于我需要合并视图的边距,所以我不得不做一些小改动。我改变了约束的优先级。

      我遇到了临时宽度和临时高度错误。所以我降低了沿水平轴的一个约束和沿垂直轴的一个约束的优先级(在本例中为顶部和尾随约束):

          let topConstraint = stackView.topAnchor.constraint(equalTo: margins.topAnchor)
          topConstraint.priority = .init(rawValue: 999.0)
          topConstraint.isActive = true
      
          let trailingConstraint = stackView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
          trailingConstraint.priority = .init(rawValue: 999.0)
          trailingConstraint.isActive = true
      

      原因是因为我用 .zero rect 初始化了我的视图,iOS 自动添加了临时的宽度和高度约束。这时候如果我们添加其他高优先级的约束,就会中断生成错误日志。

      降低优先级会修复破坏性约束,当临时约束被移除时,我们的视图将得到正确的形状。

      【讨论】:

      • 为避免此错误,请将您的 UIView 的以下属性设置为 false。 translatesAutoresizingMaskIntoConstraints = false 它不会创建 _UITemporaryLayoutWidths。
      【解决方案6】:

      只是对上述 cmets 的快速补充,对我有用的简单解决方案是更改创建约束的顺序。我以编程方式对视图和超级视图创建约束 - 但我在超级视图之前执行视图。所以我交换了它,让超视图约束首先出现,然后子视图添加了它们的约束,这意味着不再需要调用 _UITemporaryLayoutWidth,所以约束问题就消失了。

      【讨论】:

      • 为什么不在 didMoveToSuperview() 中创建所有约束?
      【解决方案7】:

      今天花了几个小时来解决这个问题。我在初始化元素后立即设置 UIButton imageView 的 contentMode,这似乎在我在顶级类中设置约束之前强制布局(其中包括约束高度/宽度)。

      【讨论】:

        【解决方案8】:

        另一种方法对我有用。

        我正在以编程方式创建我的所有视图,并将顶级视图的初始框架(如转到UIViewControllerview 属性的那个)及其子视图设置为CGRect.zero。生成的警告看起来像这样:

        "<NSLayoutConstraint:0x280ebf200 UIStackView:0x136151380.width == UILayoutGuide:0x28140e3e0'UIScrollView-frameLayoutGuide'.width - 16   (active)>",
        "<NSLayoutConstraint:0x280ddee40 '_UITemporaryLayoutWidth' MyApp.SearchFilterView:0x1316a8000.width == 0   (active)>",
        "<NSLayoutConstraint:0x280d4b480 'UIScrollView-frameLayoutGuide-width' UILayoutGuide:0x28140e3e0'UIScrollView-frameLayoutGuide'.width == MyApp.SearchFilterView:0x1316a8000.width   (active)>"
        

        您会从第二行注意到 _UITemporaryLayoutWidth 最初被设置为零。

        我一直在努力为一些子视图提供最小宽度限制,但只有给顶级视图一个非零帧删除了所有涉及_UITemporaryLayoutWidth 的自动布局警告。我专门用了UIScreen.main.bounds

        class SearchFilterView {
          
          init( ... ) {
            // Instantiate properties here...
            super.init(frame: UIScreen.main.bounds)
          }
        
        }
        

        【讨论】:

          【解决方案9】:

          我的 UITemporaryLayoutWidth 问题是我正在以编程方式创建按钮,而 tempWidth 约束将与我的宽度约束相差 0.1 左右。 (虽然很烦人,这只发生在以我创建按钮的多种方式之一创建按钮时)

          问题是我使用 .adjustsFontSizeToFitWidth = true 作为按钮标题。

          我把那行注释掉了,它就消失了。

          【讨论】:

            【解决方案10】:

            问题的根源在于 3:1 的纵横比。如果我将其更改为其他可以使用的东西,我现在使用4:1.4,它可以使用。我不知道为什么,但它有效。

            我对自动布局的内部了解不多,但我认为,问题在于布局引擎希望如何满足约束的顺序。尺寸类可能是添加临时宽度约束的原因。

            不可满足的约束似乎不是一个真正的问题,因为它们看起来只是暂时的不可满足。每次创建视图时都会出现几行日志。

            【讨论】:

              【解决方案11】:

              我认为问题是在将视图作为子视图添加到另一个视图之前向视图添加约束。如果您在没有超级视图时分配约束,它会自动添加_TemporaryLayout 约束。为了防止这种情况使translatesAutoResizingMaskIntoConstraints 为假。

              在我的例子中,我添加了一个视图控制器作为另一个视图控制器子级。即使我将 translatesAutoResizingMaskIntroConstraints 标志设置为 false 从我将其添加到父视图控制器的位置,警告仍然存在。

              然后我发现在第一次调用childViewController的视图时调用了childViewController的loadView方法。因此,当您在 childViewController 之外设置自动调整大小标志时它不起作用,因为它已经设置了它们的约束。

              父视图控制器

              let childViewController = CustomViewController()
              //childViewController.view.translatesAutoResizingMaskIntoConstraints = false 
              // This does not work since before this line executes loadView 
              
              parentViewController.addChild(childViewController)
              parentViewController.view.addSubview(childViewContoller.view) 
              // `childViewContoller.view` triggers loadView method of childViewController
              // It sets up constraints before adding as subview.
              

              解决方案是在 childViewController 中设置translatesAutoResizingMaskIntoConstraints false

              override func loadView() {
              
                   super.loadView() // If you used storyboard call this to set those views
                   // view = UIView() // call this if you layout views programatically 
                  view.translatesAutoResizingMaskIntoConstraints = false
              }
              

              【讨论】:

                【解决方案12】:

                当我创建一个视图并在其呈现之前无意中为其属性之一设置了动画时,我得到了这个。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-12-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-12-17
                  • 1970-01-01
                  • 2013-12-08
                  相关资源
                  最近更新 更多