[英]iOS UIStackView unable to simultaneously satisfy constraints if set hidden
[英]UIStackView “Unable to simultaneously satisfy constraints” on “squished” hidden views
当我的UIStackView“行”被AutoLayout
,它们将引发AutoLayout
警告。 但是,它们显示正常,除了这些日志记录之外,没有其他问题:
无法同时满足约束条件。 以下列表中至少有一个约束是您不想要的约束。 尝试以下操作:(1)查看每个约束,并尝试找出不期望的约束; (2)查找添加了一个或多个不必要约束的代码并进行修复。 (注意:如果看到的是您不了解的
NSAutoresizingMaskLayoutConstraints
,请参阅有关UIView
属性translatesAutoresizingMaskIntoConstraints
的文档)(
因此,我不确定如何解决此问题,但是除了烦人之外,它似乎并没有破坏任何东西。
有人知道如何解决吗? 有趣的是,布局约束经常用'UISV-hiding'标记,表明在这种情况下也许应该忽略子视图或某些东西的高度最小值?
之所以会出现此问题,是因为将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
,其中每个边上包含一个由8pts插入的UISegmentedControl
。
当我将其设置为hidden时,它将尝试将容器视图限制为零高度,但是由于我从上到下都有一组约束,因此发生了冲突。
为了解决该问题,我将8pt顶部底部约束的优先级从1000更改为999,因此,如果需要, UISV-hiding
约束可以优先。
我有一个类似的问题,很难解决。 就我而言,我在堆栈视图中嵌入了一个堆栈视图。 内部的UIStackView具有两个标签和指定的非零间距。
调用addArrangedSubview()时,它将自动创建类似于以下内容的约束:
V:|[innerStackView]| | = outerStackView
V:|[label1]-(2)-[label2]| | = innerStackView
现在,当您尝试隐藏innerStackView时,会收到一个模糊的约束警告。
要了解原因,首先让我们看看当innerStackView.spacing
等于0
时为什么不会发生这种情况。 当您调用innerStackView.hidden = true
,@ liamnichols是正确的…… outerStackView
将神奇地拦截此调用,并创建优先级为1000的0
高度UISV隐藏约束(必需)。 假定这是为了让堆栈视图中的元素动画化,以防万一您在UIView.animationWithDuration()
块中调用了隐藏代码。 不幸的是,似乎没有一种方法可以防止添加此约束。 但是,由于发生以下情况,您不会收到“无法同时满足约束”(USSC)警告:
很明显,可以满足这四个约束。 堆栈视图只是将所有内容拖到高度为0的像素中。
现在回到有问题的示例,如果将spacing
设置为2
,则现在具有以下约束:
堆栈视图不能都为0像素高,并且其内容不能为2像素高。 约束无法满足。
注意:您可以通过一个更简单的示例看到此行为。 只需将UIView作为已安排的子视图添加到堆栈视图即可。 然后在具有1000优先级的UIView上设置高度限制。 现在尝试在其上调用hide。
注意:无论出于何种原因,这仅在我的堆栈视图是UICollectionViewCell或UITableViewCell的子视图时发生。 但是,您仍然可以在隐藏内部堆栈视图之后,在下一个运行循环中调用innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
在单元外部重现此行为。
注意:即使您尝试在UIView.performWithoutAnimations中执行代码,堆栈视图仍将添加0高度限制,这将导致USSC警告。
至少有3种解决方案:
spacing
更改为0。这很烦人,因为每当您再次显示内容时,您都需要反转该过程(并记住原始间距)。 removeFromSuperview
。 这更令人烦恼,因为当您逆向执行此过程时,您需要记住在何处插入已删除项目。 您可以通过仅调用removeArrangedSubview然后隐藏来进行优化,但是仍有许多簿记工作需要完成。 spacing
为非零)包装在UIView中。 将至少一个约束指定为不需要的优先级(999或更低)。 这是最好的解决方案,因为您无需进行任何簿记。 在我的示例中,我在堆栈视图和包装器视图之间的顶部,顶部和结尾约束为1000,然后从堆栈视图的底部到包装器视图创建了999约束。 这样,当外部堆栈视图创建零高度约束时,999约束将被破坏,并且您不会看到USSC警告。 (注意:这类似于将UICollectionViewCell子类的contentView.translatesAutoResizingMaskToConstraints设置为false
的解决方案) 总而言之,您得到此行为的原因是:
如果Apple(1)允许您指定约束的优先级(尤其是间隔符 ),或者(2)允许您选择退出自动UISV隐藏约束,则可以轻松解决此问题。
在大多数情况下,可以通过降低约束优先级以消除冲突来解决此错误。
当您将视图设置为隐藏时, UIStackview
将尝试对其进行动画处理。 如果您想要这种效果,则需要为约束设置正确的优先级,以使它们不会发生冲突(如上文所述)。
但是,如果您不关心动画(也许您将其隐藏在ViewDidLoad中),则可以简单地执行removeFromSuperview
,效果相同,但没有任何约束问题,因为这些约束将与视图一起被删除。
根据@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()
。
首先,如其他人所建议,确保将您可以控制的约束(即不是UIStackView固有的约束)设置为优先级999,以便在隐藏视图时可以将其覆盖。
如果您仍然遇到问题,则可能是由于隐藏的StackViews中的间距所致。 我的解决方案是添加一个UIView作为分隔符,并将UIStackView的间距设置为零。 然后将View.height或View.width约束(取决于垂直或水平堆栈)设置为StackView的间距。
然后调整新添加的视图的内容拥抱和抵抗内容压缩的优先级。 您可能还必须更改父StackView的分发。
以上所有操作都可以在Interface Builder中完成。 您可能还需要以编程方式隐藏/取消隐藏一些新添加的视图,以免出现不必要的间距。
我最近在隐藏UIStackView
时遇到了自动布局错误。 不是做了一堆的记账和包装纸堆UIViews
,我选择创建我的一个出口parentStackView
和出口,因为我想隐藏/取消隐藏的孩子们。
@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!
在情节提要中,这是我的parentStack的样子:
它有4个孩子,每个孩子里面都有很多堆栈视图。 隐藏堆栈视图时,如果UI元素也是堆栈视图,则会看到一系列自动布局错误。 我选择隐藏而不是隐藏。
在我的示例中, parentStackViews
包含4个元素的数组:顶部堆栈视图,StackViewNumber1,堆栈视图编号2和停止按钮。 它们在arrangedSubviews
中的索引分别为0、1、2和3。 当我想隐藏一个,我只是从中删除parentStackView's
arrangedSubviews
阵列。 由于它并不弱,它会在内存中徘徊,您可以稍后将其放回所需的索引。 我没有重新初始化它,因此它只是一直挂到需要时才使用,但不会使内存膨胀。
所以基本上,您可以...
1)将IBOutlets拖到您的父堆栈以及要隐藏/取消隐藏的故事板上的子代。
2)当你想隐藏,删除你想隐藏栈parentStackView's
arrangedSubviews
阵列。
3)使用UIView.animateWithDuration
调用UIView.animateWithDuration
self.view.layoutIfNeeded()
。
注意最后两个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)
我发现这要比限制条件记账,摆弄优先次序等要容易得多。
如果您有默认情况下想要隐藏的内容,则可以将其放在情节view.layoutIfNeeded()
上,然后将其删除在viewDidLoad
然后使用view.layoutIfNeeded()
进行动画更新。
我在嵌入式堆栈视图中遇到了相同的错误,尽管在运行时一切正常。
我先隐藏所有子堆栈视图(设置isHidden = true
),然后再隐藏父堆栈视图,从而解决了约束错误。
这样做并没有删除子视图的全部复杂性,在需要添加子视图时维护索引。
希望这可以帮助。
您可能在使用某个尺寸等级(例如:wCompact hRegular)时创建了一个约束,然后在切换到另一个尺寸等级(例如:wAny hAny)时创建了一个重复项。 检查不同大小类中的UI对象的约束,并查看约束是否存在异常。 您应该看到红线表示冲突约束。 在获得10个信誉点之前,我无法贴图片://
我想一次隐藏整个UIStackView,但是却遇到了与OP相同的错误,这对我来说是固定的:
for(UIView *currentView in self.arrangedSubviews){
for(NSLayoutConstraint *currentConstraint in currentView.constraints){
[currentConstraint setPriority:999];
}
}
我有一排带有高度限制的按钮。 当一个按钮被隐藏时,会发生这种情况。 将按钮高度限制的优先级设置为999可解决此问题。
有道理为上述问题的根源提供了一个很好的答案,因此我将直接解决。
您需要做的就是将所有stackView约束的优先级设置为低于1000(999可以完成工作)。 例如,如果将stackView限制在其超级视图的左,右,上和下,则所有4个约束的优先级均应低于1000。
此错误与UIStackView没有关系。 当您具有相同优先级的冲突约束时,就会发生这种情况。 例如,如果有一个约束说明视图的宽度为100,而同时有另一个约束则说明视图的宽度为其容器的25%。 显然有两个相互矛盾的约束。 解决的办法是删除它们。
使用[mySubView removeFromSuperview]进行NOP。 我希望它可以帮助某人:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.