How can I change constraints priority in run time
IosObjective CAutolayoutConstraintsIos Problem Overview
I have a view which has dynamic height and I am trying to change this view height priority in run time.
Here is my part of code;
if (index == 0) {
surveyViewHeightConstraint.constant = 0;
surveyViewHeightConstraint.priority = 1000;
} else if (index == 1) {
surveyViewHeightConstraint.constant = 163;
surveyViewHeightConstraint.priority = 500;
}
I am changing index with a button action. When I run this code, I am getting this error:
*** Assertion failure in -[NSLayoutConstraint setPriority:], /SourceCache/Foundation/Foundation-1141.1/Layout.subproj/NSLayoutConstraint.m:174
What is my mistake in here?
Ios Solutions
Solution 1 - Ios
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.
Solution 2 - Ios
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)
Solution 3 - Ios
Swift version:
myContraint.priority = UILayoutPriority(999.0)
Solution 4 - Ios
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;
Solution 5 - Ios
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.
Solution 6 - Ios
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])
}
Solution 7 - Ios
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
Solution 8 - Ios
Now you can , just take the IBOutlet connection for your constraints , then use this code .
self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)