UIStackView"无法同时满足约束条件"在"压扁"隐藏的意见

时间:2015-09-06 20:54:06

标签: ios autolayout uikit ios9 uistackview

当我的UIStackView"行"被压扁,他们会发出AutoLayout警告。但是,它们显示正常,除了这些记录之外别无其他错误:

  

无法同时满足约束条件。       可能至少以下列表中的一个约束是您不想要的约束。试试这个:(1)看看每个约束,并试着找出你不期望的东西; (2)找到添加了不需要的约束或约束的代码并修复它。 (注意:如果您看到NSAutoresizingMaskLayoutConstraints您不明白,请参阅UIView属性translatesAutoresizingMaskIntoConstraints的文档)   (

所以,我还不确定如何解决这个问题,但除了讨厌之外,似乎并没有打破任何东西。

有谁知道如何解决它?有趣的是,布局约束经常使用' UISV隐藏' 进行标记,表明它可能会忽略子视图或此实例中的某些内容的高度最小值?

14 个答案:

答案 0 :(得分:194)

您遇到此问题是因为在将UIStackView内的子视图设置为隐藏时,它会首先将其高度限制为零,以便将其设置为动画。

我收到以下错误:

2015-10-01 11:45:13.732 <redacted>[64455:6368084] 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:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

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.

我尝试做的是在我的UIView内放置一个UIStackView,每个边缘包含UISegmentedControl插入8磅。

当我将其设置为隐藏时,它会尝试将容器视图约束到零高度,但因为我从上到下有一组约束,所以存在冲突。

要解决此问题,我将我的8pt顶部约束优先级从1000更改为999,以便UISV-hiding约束可以在需要时优先使用。

答案 1 :(得分:46)

我遇到了一个不容易解决的类似问题。就我而言,我在堆栈视图中嵌入了堆栈视图。内部UIStackView有两个标签,并指定了非零间距。

当你调用addArrangedSubview()时,它会自动创建类似于以下内容的约束:

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

现在,当您尝试隐藏innerStackView时,会出现不明确的约束警告。

为了理解原因,让我们首先看看当innerStackView.spacing等于0的原因。当您致电innerStackView.hidden = true时,@ liamnichols是正确的...... outerStackView会神奇地拦截此次通话,并创建0高度 UISV隐藏约束优先级1000(必填)。据推测,这是为了允许在UIView.animationWithDuration()块中调用隐藏代码的情况下将堆栈视图中的元素设置为动画。不幸的是,似乎没有办法阻止添加这种约束。然而,由于发生以下情况,您将不会得到“无法同时满足约束”(USSC)警告:

  1. label1的高度设置为0
  2. 两个标签之间的间距已经定义为0
  3. label2的高度设置为0
  4. innerStackView的高度设置为0
  5. 很明显,可以满足这4个约束条件。堆栈视图只是将所有内容都扫描成0高度的像素。

    现在回到有缺陷的示例,如果我们将spacing设置为2,我们现在有了这些限制:

    1. label1的高度设置为0
    2. 堆叠视图自动创建两个标签之间的间距为2像素高,优先级为1000。
    3. label2的高度设置为0
    4. innerStackView的高度设置为0
    5. 堆栈视图不能都是0像素高,其内容高2像素。不能满足约束条件。

      注意:您可以通过一个更简单的示例来查看此行为。只需将UIView添加到堆栈视图作为排列的子视图。然后在该UIView上设置一个高度约束,优先级为1000。现在尝试在上面调用hide。

      注意:无论出于何种原因,这只发生在我的堆栈视图是UICollectionViewCell或UITableViewCell的子视图时。但是,您仍然可以通过在隐藏内部堆栈视图后在下一个运行循环中调用innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)来在单元格外重现此行为。

      注意:即使您尝试在UIView.performWithoutAnimations中执行代码,堆栈视图仍会添加0高度约束,这将导致USSC警告。

      此问题至少有3个解决方案:

      1. 在隐藏堆栈视图中的任何元素之前,检查它是否是堆栈视图,如果是,请将spacing更改为0. 这很烦人,因为您需要撤消该过程(并记住原始间距)每当您再次显示内容时。
      2. 不要将元素隐藏在堆栈视图中,而是调用removeFromSuperview这更令人讨厌,因为当您撤消流程时,您需要记住 where 插入删除的项目。您可以通过仅调用removeArrangedSubview然后隐藏来进行优化,但仍有大量的簿记需要完成。
      3. 在UIView中包装嵌套的堆栈视图(具有非零spacing)。将至少一个约束指定为非必需优先级(999或更低)。这是最佳解决方案,因为您不必进行任何簿记。在我的示例中,我在堆栈视图和包装器视图之间创建了1000的顶部,前导和尾随约束,然后从堆栈视图的底部到包装器视图创建了一个999约束。这样,当外部堆栈视图创建零高度约束时,999约束将被破坏,并且您不会看到USSC警告。 (注意:这类似于Should the contentView.translatesAutoResizingMaskToConstraints of a UICollectionViewCell subclass be set to false
      4. 的解决方案

        总之,您获得此行为的原因是:

        1. 当您将托管子视图添加到堆栈视图时,Apple会自动为您创建1000个优先级约束。
        2. 当您隐藏堆栈视图的子视图时,Apple会自动为您创建0高度约束。
        3. 如果Apple(1)允许您指定约束的优先级(尤其是间隔符),或者(2)允许您选择退出自动 UISV隐藏约束,这个问题很容易解决。

答案 2 :(得分:5)

大多数情况下,可以通过降低约束优先级来解决此错误,以消除冲突。

答案 3 :(得分:2)

当您将视图设置为隐藏时,UIStackview会尝试将其设置为动画。如果你想要这种效果,你需要为约束设置正确的优先级,这样它们就不会发生冲突(正如上面提到的那样)。

但是,如果您不关心动画(也许您正在将其隐藏在ViewDidLoad中),那么您可以简单removeFromSuperview这将具有相同的效果但没有任何约束问题,因为这些将被删除以及观点。

答案 4 :(得分:1)

根据@ Senseful的回答,这里是一个UIStackView扩展,用于在视图中包装堆栈视图并应用他或她建议的约束:

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

请使用stackView

,而不是添加stackView.wrapped()

答案 5 :(得分:1)

首先,正如其他人所建议的那样,确保您可以控制的约束,即UIStackView固有的约束设置为优先级999,以便在隐藏视图时覆盖它们。

如果您仍然遇到此问题,则问题可能是隐藏的StackViews中的间距。 我的解决方案是添加一个UIView作为间隔符并将UIStackView间距设置为零。然后将View.height或View.width约束(取决于垂直或水平堆栈)设置为StackView的间距

然后调整新添加的视图的内容拥抱和内容压缩阻力优先级。您可能还必须更改父StackView的分布。

以上所有操作都可以在Interface Builder中完成。您可能还需要以编程方式隐藏/取消隐藏一些新添加的视图,这样您就不会有不必要的间距。

答案 6 :(得分:1)

我最近在隐藏UIStackView时遇到了自动布局错误。我没有在UIViews中做一堆簿记和包装堆栈,而是选择为我的parentStackView创建一个插座,为我想要隐藏/取消隐藏的孩子创建插座。

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

在故事板中,这是我的parentStack的样子:

enter image description here

它有4个孩子,每个孩子都有一堆堆栈视图。当您隐藏堆栈视图时,如果它的UI元素也是堆栈视图,您将看到自动布局错误的流。我没有隐藏,而是选择删除它们。

在我的示例中,parentStackViews包含4个元素的数组:Top Stack View,StackViewNumber1,Stack View Number 2和Stop Button。它们在arrangedSubviews中的指数分别为0,1,2和3。当我想隐藏一个时,我只是将其从parentStackView's arrangedSubviews数组中删除。由于它并不弱,它会在内存中徘徊,你可以稍后将它放回到你想要的索引。我不是要重新初始化它,所以它只是在需要之前就会挂起,但不会让内存膨胀。

基本上,你可以......

1)将IBOutlets拖动到您的父堆栈以及要隐藏/取消隐藏到故事板的子项。

2)如果要隐藏它们,请从parentStackView's arrangedSubviews数组中删除要隐藏的堆栈。

3)使用self.view.layoutIfNeeded()致电UIView.animateWithDuration

注意最后两个stackViews不是weak。当你取消隐藏它们时,你需要保留它们。

假设我想隐藏stackViewNumber2:

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

然后给它制作动画:

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

如果您想稍后“取消隐藏”stackViewNumber2,则可以将其插入所需的parentStackView arrangedSubViews索引并为更新设置动画。

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

我发现这比在约束条件下记账,摆弄优先事项等要容易得多。

如果您希望隐藏某些内容,则可以将其放在故事板上并在viewDidLoad中将其删除,并使用view.layoutIfNeeded()在没有动画的情况下进行更新。

答案 7 :(得分:1)

我在嵌入式堆栈视图中遇到了相同的错误,尽管在运行时一切正常。

我通过在隐藏父堆栈视图之前先隐藏所有子堆栈视图(设置isHidden = true)来解决约束错误。

执行此操作并不具备删除子排列视图的所有复杂性,在需要添加它们时维护索引。

希望这会有所帮助。

答案 8 :(得分:0)

您可能在使用特定大小类时创建了约束(例如:wCompact hRegular),然后在切换到另一个大小类时创建了一个副本(例如:wAny hAny)。检查不同大小类中的UI对象的约束,并查看是否存在约束异常。你应该看到红线表示碰撞的约束。在得到10点声望之前,我无法拍照留念:/

答案 9 :(得分:0)

我想一次隐藏整个UIStackView,但我得到了与OP相同的错误,这为我解决了这个问题:

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}

答案 10 :(得分:0)

我有一排高度限制的按钮。隐藏一个按钮时会发生这种情况。将该按钮高度约束的优先级设置为999已解决了该问题。

答案 11 :(得分:0)

有理智为上述问题的根源提供了一个很好的答案,因此我将直接解决。

您需要做的就是将所有stackView约束的优先级设置为低于1000(999可以完成工作)。例如,如果将stackView限制在其超级视图的左,右,顶部和底部,则所有4个约束的优先级均应低于1000。

答案 12 :(得分:-2)

此错误与UIStackView无关。当您有相同优先级的冲突约束时,就会发生这种情况。例如,如果您有一个约束状态,视图的宽度为100,并且您同时有另一个约束,则表明视图的宽度是其容器的25%。显然有两个相互矛盾的约束。解决方案是删除它们。

答案 13 :(得分:-2)

使用[mySubView removeFromSuperview]进行NOP。 我希望它可以帮助某人:)