什么是_UITemporaryLayoutWidth以及为什么它会破坏我的约束?

时间:2015-06-21 21:09:07

标签: ios autolayout nslayoutconstraint

我有一个包含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 constaint来自哪里?显然我没有添加它。谷歌没有吐出任何有用_UITemporaryLayoutWidth的内容。有没有人遇到这样的问题?

12 个答案:

答案 0 :(得分:55)

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

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

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

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

更新3:所以,我再次遇到这个问题,想要分享更多信息。我的情况是我试图基本上使用systemLayoutSizeFittingSize:方法测量没有父级的视图。简而言之,我必须在测量之前在视图上调用layoutIfNeeded - 这是我遇到临时约束冲突的地方,因为视图没有超视图。临时约束常量(维度)确实对应于没有超视图的视图上设置的任何帧 - 我认为这是有道理的(但是,在将要与AutoLayout一起使用的视图上设置框架似乎很奇怪.. 。)我能够解决临时约束问题 - 如果视图没有超视图,那么将其添加到临时超视图中;测量;从临时superview中删除它。希望这很有用。

答案 1 :(得分:15)

如上所述,临时约束似乎是在没有父视图的视图上设置的。我发现在顶级视图中确保translatesAutoresizingMaskIntoConstraintsfalse会删除临时约束。为:

topView.translatesAutoresizingMaskIntoConstraints = false

答案 2 :(得分:8)

我的情况,问题如下:

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

这造成了错误&#34; Unable to simultaneously satisfy constraints&#34;其中一个冲突的约束是_UITemporaryLayoutHeight

当我将按钮添加到超级视图之前设置其宽度和高度限制时,错误就消失了。

答案 3 :(得分:2)

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

答案 4 :(得分:2)

在我的情况下,我在这种情况下只有这个UITemporaryLayoutWidth冲突 1我用[UIView new]创建一个视图,我没有将它添加到任何superview 2然后我为这个视图创建一个宽度约束 3立即调用layoutIfNeeded到此视图,并发生冲突

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

答案 5 :(得分:1)

我的UITemporaryLayoutWidth问题是我以编程方式创建按钮,tempWidth约束约为我的宽度约束0.1。 (虽然令人讨厌,这只发生在我创建按钮的多种方式之一中创建按钮时)

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

我注意到这条线并且它消失了。

答案 6 :(得分:1)

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

答案 7 :(得分:1)

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

我正在使用.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自动添加了临时的宽度和高度约束。此时,如果我们添加其他具有较高优先级的约束,则会中断生成错误日志的过程。

降低优先级可以解决破坏性约束,当除去临时约束时,我们的视图将具有正确的形状。

答案 8 :(得分:1)

一种不同的方法对我有用。

我正在以编程方式创建所有视图,并将顶级视图的初始框架(如转到 viewUIViewController 属性的框架)及其子视图设置为 {{ 1}}。生成的警告如下所示:

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

答案 9 :(得分:0)

问题的根源是3:1的宽高比。如果我将其更改为其他功能,我现在使用4:1.4,它可以正常工作。我不知道为什么,但它确实有效。

我不太了解自动布局的内部结构,但我认为,问题在于布局引擎如何实现约束。大小类可能是添加临时宽度约束的原因。

不可满足的限制似乎不是一个真正的问题,因为它看起来只是暂时不可满足。每次创建视图时,只会显示几行日志。

答案 10 :(得分:0)

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

就我而言,我正在添加一个视图控制器作为另一个视图控制器子级。甚至在我将translatesAutoResizingMaskIntroConstraints标志添加到父视图控制器的地方都将其设置为false时,警告仍然存在。

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

ParentViewController

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为假

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
}

答案 11 :(得分:0)

我在创建视图并在其呈现之前无意中对其属性之一进行动画处理时得到了这个。