How can I change constraints priority in run time

IosObjective CAutolayoutConstraints

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLe'KirdokView Question on Stackoverflow
Solution 1 - IosCyrilleView Answer on Stackoverflow
Solution 2 - IosVipinView Answer on Stackoverflow
Solution 3 - IosAlexView Answer on Stackoverflow
Solution 4 - IosJ Andrew McCormickView Answer on Stackoverflow
Solution 5 - Iosuser5463574View Answer on Stackoverflow
Solution 6 - IosJackView Answer on Stackoverflow
Solution 7 - IosVenkat057View Answer on Stackoverflow
Solution 8 - IosKishore KumarView Answer on Stackoverflow