98

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?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Kirdok
  • 1,904
  • 2
  • 22
  • 27

8 Answers8

192

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.

Cyrille
  • 25,014
  • 12
  • 67
  • 90
  • 5
    Thanks for your nice answer, but if i use 999 instead of 1000, i can't hide my view by assigning 0 to height constraint. – Kirdok Jul 02 '15 at 14:05
  • That's another problem. You may have other conflicting constraints. If your code does not crash anymore but your view animation is still incorrect, please mark this question as resolved; then open another one. – Cyrille Jul 02 '15 at 14:07
  • nice one, it's not a problem to use different values (999, 900, 500, etc) :) – user924 Mar 19 '18 at 10:14
  • 7
    Seriously? Is there anything working just normally in iOS development? So many things had to be done manually and/or had to implement tons of workaround code cause of system bugs or unsupportedness. Sorry for not constructive comment. – Luten May 10 '18 at 12:23
  • 13
    It seems like this limitation has been removed in iOS 13. I tried changing a constraint from `required` to `defaultLow` and it worked. The same code used to crash on iOS 12. – Steven Vandeweghe Jun 27 '19 at 09:00
  • I can confirm this limitation has been remove on iOS 13. – tperei May 11 '20 at 20:44
  • yup removed from iOS 13 onwards. – Reckoner May 20 '21 at 14:33
  • This limitation exists in macOS 10.14 by the way, possibly removed in 10.15 onwards – strangetimes Jun 02 '21 at 02:44
  • Issue exists for iOS 12.0 and earlier only, and the suggested fix works fine. Change the priority from 1000 to 999 and it will not crash the app. – Dhaval H. Nena Oct 05 '21 at 07:27
58

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)
Ram Madhavan
  • 2,362
  • 1
  • 17
  • 20
Vipin
  • 1,725
  • 1
  • 14
  • 13
16

Swift version:

myContraint.priority = UILayoutPriority(999.0)
Alex
  • 1,155
  • 1
  • 8
  • 12
8

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;
6

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.

2

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

Endanke
  • 867
  • 13
  • 24
Venkat057
  • 187
  • 9
1

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])
        
         }  
Jack
  • 13,571
  • 6
  • 76
  • 98
  • 2
    No, you should never create constraints in `viewDidLayoutSubviews`. That method can be called several times, which can crash your app if you create duplicate constraints there. – mph Aug 20 '17 at 15:35
-1

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

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)
Kishore Kumar
  • 4,265
  • 3
  • 26
  • 47