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

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

这是我的部分代码;

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

我在这里犯了什么错?

70744 次浏览

As stated in NSLayoutConstraint class reference:

Priorities may not change from nonrequired to required, or from required to nonrequired. An exception will be thrown if a priority of NSLayoutPriorityRequired in OS X or UILayoutPriorityRequired in iOS is changed to a lower priority, or if a lower priority is changed to a required priority after the constraints is added to a view. Changing from one optional priority to another optional priority is allowed even after the constraint is installed on a view.

Use priority 999 instead of 1000. It won't be absolutely required technically speaking, but it'll be a higher priority than anything else.

The way we have always handled this is by not changing the constraint constant, just the priority. For example, in your situation have two height constraints.

 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)

if you want to hide the view, you:

heightConstraintTwo.priority = 999;

likewise, if you want to show the view:

heightConstraintTwo.priority = 250;

I want to make a small addition to Cyrille's answer.

If you are creating constraint in code, make sure to set its priority before making it active. For instance:

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

This would result in run-time exception.

Mutating a priority from required to not on an installed constraint (or vice-versa) is not supported.

Correct order is:

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

For Swift version 4+

constraintName.priority = UILayoutPriority(rawValue: 999)

According to NSLayoutConstraints class inside UIKit Module

If a constraint's priority level is less than UILayoutPriorityRequired, then it is optional. Higher priority constraints are met before lower priority constraints. Constraint satisfaction is not all or nothing. If a constraint 'a == b' is optional, that means we will attempt to minimize 'abs(a-b)'. This property may only be modified as part of initial set up or when optional. After a constraint has been added to a view, an exception will be thrown if the priority is changed from/to NSLayoutPriorityRequired.

Example:- UIButton constraints with various priorities -

 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])
        

}

Swift version:

myContraint.priority = UILayoutPriority(999.0)

Now you can , just take the IBOutlet connection for your constraints , then use this code .

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)

I hope this scenario help someone: I had two constraints, that they were opposite to each other, I mean they couldn't be satisfied in the same time, in interface builder one of them had 1000 priority and other had less than 1000 priority. when I changed them in the code, I had this error:

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.'

then I just initialized them in viewDidLoad function with .defaultHigh and .defaultLow values and this fixed my problem.

I was facing the same issue. As some of the answer mentioned above in iOS 13 changing priority will work fine, However in iOS 12 it will lead to crash.

I was able to fix this problem by creating IBOutlet NSLayoutConstraint to that specific constraint in Storyboard retaining its priority to 1000, below is the code for fix.

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

Hope this helps!!! Cheers