嵌套的UIStack查看破坏的约束

Ale*_*pov 27 cocoa-touch interface-builder ios autolayout uistackview

我有一个复杂的视图层次结构,内置在Interface Builder中,具有嵌套的UIStackViews.每次隐藏一些内部堆栈视图时,我都会收到"不满意的约束"通知.我跟踪到了这个:

(
    "<NSLayoutConstraint:0x1396632d0 'UISV-canvas-connection' UIStackView:0x1392c5020.top == UILabel:0x13960cd30'Also available on iBooks'.top>",
    "<NSLayoutConstraint:0x139663470 'UISV-canvas-connection' V:[UIButton:0x139554f80]-(0)-|   (Names: '|':UIStackView:0x1392c5020 )>",
    "<NSLayoutConstraint:0x139552350 'UISV-hiding' V:[UIStackView:0x1392c5020(0)]>",
    "<NSLayoutConstraint:0x139663890 'UISV-spacing' V:[UILabel:0x13960cd30'Also available on iBooks']-(8)-[UIButton:0x139554f80]>"
)
Run Code Online (Sandbox Code Playgroud)

具体来说,UISV-spacing约束:当隐藏UIStackView时,它的高约束得到一个0常量,但这似乎与内部stackview的间距约束发生冲突:它需要我的Label和Button之间有8个点,这与隐藏约束是不可调和的,因此约束崩溃.

有没有解决的办法?我已经尝试递归隐藏隐藏堆栈视图的所有内部StackViews,但这导致奇怪的动画,其中内容浮出屏幕,并导致严重的FPS下降启动,同时仍然没有解决问题.

Sen*_*ful 25

这是隐藏嵌套堆栈视图的已知问题.

这个问题基本上有3个解决方案:

  1. 将间距更改为0,但是您需要记住之前的间距值.
  2. 调用innerStackView.removeFromSuperview(),但是你需要记住插入堆栈视图的位置.
  3. 在具有至少一个999约束的UIView中包装堆栈视图.例如,顶级@ 1000,领先@ 1000,尾随@ 1000,底部@ 999.

在我看来,第三种选择是最好的.有关此问题的更多信息,为什么会发生,不同的解决方案以及如何实现解决方案3,请参阅我对类似问题的回答.

  • 解决方案3对我有用,我把堆栈视图放在一个UIView里面,约束到所有边缘(就像普通边缘一样).看起来堆栈视图不像是根视图. (5认同)

Jaa*_*nus 19

我在UISV隐藏方面遇到了类似的问题.对我来说,解决方案是将我自己的约束优先级从Required(1000)减少到小于此值.添加UISV隐藏约束时,它们优先,约束不再发生冲突.

  • 改变优先事项对我也有用.此外,删除从未使用的大小类中意外复制的任何多余(变暗)约束.重要提示:要更轻松地调试这些问题,请在每个约束上设置IDENTIFER字符串.然后你可以在调试消息中看到哪个Constraint是顽皮的. (5认同)
  • 问题是我对这些元素没有任何限制:(但是,这确实解决了我所做的情况下的其他类似问题. (3认同)

rob*_*off 18

所以,你有这个:

破碎的动画

问题是,当您第一次折叠内部堆栈时,会出现自动布局错误:

2017-07-02 15:40:02.377297-0500 nestedStackViews[17331:1727436] [LayoutConstraints] 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. 
(
    "<NSLayoutConstraint:0x62800008ce90 'UISV-canvas-connection' UIStackView:0x7fa57a70fce0.top == UILabel:0x7fa57a70ffb0'Top Label of Inner Stack'.top   (active)>",
    "<NSLayoutConstraint:0x62800008cf30 'UISV-canvas-connection' V:[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...']-(0)-|   (active, names: '|':UIStackView:0x7fa57a70fce0 )>",
    "<NSLayoutConstraint:0x62000008bc70 'UISV-hiding' UIStackView:0x7fa57a70fce0.height == 0   (active)>",
    "<NSLayoutConstraint:0x62800008cf80 'UISV-spacing' V:[UILabel:0x7fa57a70ffb0'Top Label of Inner Stack']-(8)-[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...']   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x62800008cf80 'UISV-spacing' V:[UILabel:0x7fa57a70ffb0'Top Label of Inner Stack']-(8)-[UILabel:0x7fa57d30def0'Bottom Label of Inner Sta...']   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Run Code Online (Sandbox Code Playgroud)

正如您所指出的,问题是外部堆栈视图将height = 0约束应用于内部堆栈视图.这与内部堆栈视图在其自己的子视图之间应用的8点填充约束冲突.两种约束都不能同时满足.

我相信外部堆栈视图使用此height = 0约束,因为它在动画时看起来比仅让内部视图隐藏而不先收缩更好.

有一个简单的解决方法:将内部堆栈视图包装在一个平面中UIView,并隐藏该包装器.我会证明.

这是上面破碎版本的场景轮廓:

破碎的轮廓

要解决此问题,请选择内部堆栈视图.从菜单栏中,选择"编辑器">"嵌入">"查看":

嵌入视野中

当我这样做时,Interface Builder在包装器视图上创建了一个宽度约束,因此删除该宽度约束:

删除宽度约束

接下来,在包装器的所有四个边和内部堆栈视图之间创建约束:

创造约束

此时,布局在运行时实际上是正确的,但Interface Builder会错误地绘制它.您可以通过将内部堆栈的子项的垂直拥抱优先级设置得更高来修复它.我把它们设置为800:

拥抱优先事项

到目前为止,我们还没有解决不可满足的约束问题.为此,请找到刚刚创建的底部约束,并将其优先级设置为低于要求.我们把它改成800:

改变底部约束优先级

最后,您可能在视图控制器中有一个连接到内部堆栈视图的插座,因为您正在更改其hidden属性.将该出口更改为连接到包装器视图而不是内部堆栈视图.如果您的插座类型是UIStackView,您需要将其更改为UIView.我已经是类型UIView,所以我只是在故事板中重新连接它:

改变出口

现在,当您切换包装器视图的hidden属性时,堆栈视图将显示为折叠,没有不可满足的约束警告.它看起来几乎完全相同,所以我不打算发布另一个运行的应用程序的GIF.

您可以在此github存储库中找到我的测试项目.


小智 16

理想情况下,我们可以将UISV-spacing约束的优先级设置为较低的值,但似乎没有任何方法可以做到这一点.:)

spacing在隐藏之前将嵌套堆栈视图的属性设置为0 成功,并在使其再次可见后恢复到正确的值.

我认为在嵌套堆栈视图上递归执行此操作会起作用.您可以将spacing属性的原始值存储在字典中,然后再将其还原.

我的项目只有一个级别的嵌套,所以我不确定这是否会导致FPS问题.只要你没有设置间距变化的动画,我认为它不会造成太多的打击.