如何在运行时更改约束优先级

98

我有一个高度动态变化的视图,我想在运行时更改这个视图的高度优先级。

以下是我的部分代码:

if (index == 0) {

    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.priority = 1000;

} else if (index == 1) {

    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.priority = 500;

}

我正在使用按钮操作更改索引。运行此代码时,我遇到了以下错误:

*** Assertion failure in -[NSLayoutConstraint setPriority:], /SourceCache/Foundation/Foundation-1141.1/Layout.subproj/NSLayoutConstraint.m:174

这里我犯了什么错误?

8个回答

192
NSLayoutConstraint类别参考文献所述:

优先级不能从非必需更改为必需,也不能从必需更改为非必需。如果在将约束添加到视图后将OS X中的NSLayoutPriorityRequired或iOS中的UILayoutPriorityRequired的优先级更改为较低优先级,则会抛出异常,或者如果将较低优先级更改为必需优先级,则会抛出异常。即使在将约束安装在视图上后,也允许从一个可选优先级更改为另一个可选优先级。

使用优先级999而不是1000。从技术上讲,这并不绝对要求,但它比其他任何东西的优先级都要高。


5
谢谢您的回答。但是,如果我使用999而不是1000,那么我就无法通过将高度约束分配为0来隐藏我的视图。 - Le'Kirdok
这是另一个问题。您可能有其他冲突的约束条件。如果您的代码不再崩溃,但您的视图动画仍然不正确,请将此问题标记为已解决;然后打开另一个问题。 - Cyrille
7
说真的吗?在iOS开发中有什么是正常工作的吗? 由于系统漏洞或不支持,很多事情都必须手动完成和/或实施大量的解决方法代码。抱歉我没有给出建设性的评论。 - Luten
13
在iOS 13中似乎取消了这个限制。我尝试将一个约束从“required”更改为“defaultLow”,它成功了。相同的代码在iOS 12上曾经崩溃过。 - Steven Vandeweghe
我可以确认在iOS 13上已经移除了这个限制。 - tperei
显示剩余4条评论

58

我想对Cyrille的答案进行小小的补充。

如果你在代码中创建约束,请确保在激活约束之前设置其优先级。例如:

surveyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self
                                               attribute:NSLayoutAttributeHeight
                                               relatedBy:NSLayoutRelationEqual
                                                  toItem:self.superview
                                               attribute:NSLayoutAttributeHeight
                                              multiplier:1
                                                constant:0];
surveyViewHeightConstraint.active = YES;
surveyViewHeightConstraint.priority = 999;

这将导致运行时异常。

修改已安装的约束条件的优先级(从必需更改为非必需或反之)是不受支持的。

正确的顺序是:

surveyViewHeightConstraint.priority = 999;
surveyViewHeightConstraint.active = YES;

适用于Swift 4版本及以上

constraintName.priority = UILayoutPriority(rawValue: 999)

16

Swift版本:

myContraint.priority = UILayoutPriority(999.0)

8
我们一直处理这个问题的方式是不改变约束常量,只调整优先级。例如,在您的情况下有两个高度约束。
 heightConstraintOne (who's height is set in the storyboard at 150, and priority set at 750)
 heightConstraintTwo (who's height is set in the storyboard at 0, and priority set at 250)

如果你想隐藏该视图,则可以:
heightConstraintTwo.priority = 999;

同样地,如果你想展示这个视图:
heightConstraintTwo.priority = 250;

5
明白了:使用999而不是1000。谢谢。 - brainray

6
我希望这个场景对某些人有所帮助: 我有两个限制条件,它们互相矛盾,也就是说它们不能同时满足,在界面构建器中,其中一个的优先级为1000,另一个的优先级小于1000。当我在代码中更改它们时,出现了以下错误:
"Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Mutating a priority from required to not on an installed constraint (or vice-versa) is not supported. You passed priority 250 and the existing priority was 1000.'"
然后,我只需在viewDidLoad函数中使用.defaultHigh和.defaultLow值进行初始化即可解决问题。

2

我曾面临同样的问题。正如上面的一些答案在iOS 13中更改优先级可以正常工作,但是在iOS 12中会导致崩溃。

我通过在Storyboard中创建IBOutlet NSLayoutConstraint来解决此问题,并将其优先级保留为1000,以下是修复代码。

if (Condition) {
    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.isActive = false;
} else if (index == 1) {
    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.isActive = True;
}

希望这有所帮助!!! 干杯

1
根据 UIKit 模块内的 NSLayoutConstraints 类,如果约束的优先级小于 UILayoutPriorityRequired,则它是可选的。高优先级的约束会在低优先级约束之前满足。约束的满足不是全有或全无的。如果约束“a == b”是可选的,这意味着我们将尝试最小化“abs(a-b)”。 此属性只能在初始设置或可选时修改。在将约束添加到视图后,如果将优先级从/到 NSLayoutPriorityRequired 更改,将引发异常。 示例:带有不同优先级的 UIButton 约束。
 func setConstraints() {
        buttonMessage.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: buttonMessage, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: -10).isActive = true
        
        let leading = NSLayoutConstraint(item: buttonMessage, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 10)
        
        leading.isActive = true
        
        
        let widthConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)
        
        let heightConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
        
        
        let trailingToSuperView = NSLayoutConstraint(item: buttonMessage, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
        
        trailingToSuperView.priority = 999
        trailingToSuperView.isActive = true
        
        //leading.isActive = false//uncomment & check it will align to right of the View
        
        buttonMessage.addConstraints([widthConstraint,heightConstraint])
        
         }  

2
不,你绝不能在 viewDidLayoutSubviews 中创建约束。该方法可能会被调用多次,在那里创建重复的约束可能会导致应用程序崩溃。 - mph

-1
现在你可以,只需要为你的约束取一个 IBOutlet 连接,然后使用这段代码。
self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接