62

I am new in auto layout. I have done all of my project from xib file, but now I faced a problem where I have to update an view's height programmatically. I have tried below but now working.

[[self view] addConstraint:[NSLayoutConstraint constraintWithItem:loginContainer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:loginFrame.size.height]];

In console it's shows

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x78724530 V:[UIView:0x790cdfb0(170)]>",
    "<NSLayoutConstraint:0x787da210 V:[UIView:0x790cdfb0(400)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x78724530 V:[UIView:0x790cdfb0(170)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Krunal
  • 77,632
  • 48
  • 245
  • 261
Tapas Pal
  • 7,073
  • 8
  • 39
  • 86
  • 4
    Instead of adding an entirely new constraint, why not just update the existing one? Create an IBOutlet to the existing constraint and alter the `constant` value. – j.f. May 05 '15 at 18:24
  • 2
    You should make an IBOutlet to the height constraint you made in IB, and modify its `constant` property in code. – rdelmar May 05 '15 at 18:26
  • this `loginContainer` is a subview of a view which is loaded from `[NSBundle mainBundle] loadNibName:` how could I create an IBOutlet here? – Tapas Pal May 05 '15 at 18:29
  • @j.f. how to update an existing constraint?? can you show me. – Tapas Pal May 06 '15 at 05:07
  • @TapasPal, the answers already given below are what I was going to suggest. – j.f. May 06 '15 at 12:35
  • Yes I had use it, I just want to avoid the loops, though it's look an inexpensive operation. – Tapas Pal May 06 '15 at 12:44

5 Answers5

123

Instead of adding a new constraint, you need to modify the constant on your existing constraint.

Use an IBOutlet to connect to your constraint in Interface Builder:

@property (nonatomic, weak) NSLayoutConstraint *heightConstraint;

Then, when you need to set it programmatically, simply set the constant property on the constraint:

heightConstraint.constant = 100;

OR

If you can't access the nib in Interface Builder, find the constraint in code:

NSLayoutConstraint *heightConstraint;
for (NSLayoutConstraint *constraint in myView.constraints) {
    if (constraint.firstAttribute == NSLayoutAttributeHeight) {
        heightConstraint = constraint;
        break;
    }
}
heightConstraint.constant = 100;

And in Swift:

if let constraint = (myView.constraints.filter{$0.firstAttribute == .width}.first) {
            constraint.constant = 100.0
        }
Obliquely
  • 7,002
  • 2
  • 32
  • 51
Dave Paul
  • 1,341
  • 1
  • 9
  • 7
  • I can't create this because this loginContainer get loaded from a external xib file which is not a member class of self. – Tapas Pal May 05 '15 at 18:32
  • If you don't already have a reference to the constraint you need, looping through all of the view's constraints is the only way to get the reference you need. But considering you should only ever have a handful of constraints belonging to a particular view, and you're just checking an enum value, this a VERY inexpensive loop. – Dave Paul May 06 '15 at 11:18
  • thanks for you reply. I'll accept your answer but let see first if any other good answer will come or not. – Tapas Pal May 06 '15 at 11:33
  • Awesome answer! Thank you!! – dmlebron May 24 '16 at 15:35
  • 7
    Attention. The condition is insufficient, it should be constraint.firstAttribute == NSLayoutAttributeHeight && constraint.secondAttribute == NSLayoutAttributeNotAnAttribute to exclude getting ratio constraint – HotJard Jun 03 '16 at 09:32
23

To get a reference of your height constraints : Click + Ctrl in the constraint and drag and drop in your class file :

enter image description here

To update constraint value :

self.heightConstraint.constant = 300;
[self.view updateConstraints];
HamzaGhazouani
  • 6,464
  • 6
  • 33
  • 40
18

A more flexible way using this swift extension:

extension UIView {    

    func updateConstraint(attribute: NSLayoutAttribute, constant: CGFloat) -> Void {
        if let constraint = (self.constraints.filter{$0.firstAttribute == attribute}.first) {
            constraint.constant = constant
            self.layoutIfNeeded()
        }
    }
}

How to use this view extension to update constant value of existing constraints:

// to update height constant
testView.updateConstraint(attribute: NSLayoutAttribute.height, constant: 20.0)


// to update width constant
testView.updateConstraint(attribute: NSLayoutAttribute.width, constant: 20.0)
Krunal
  • 77,632
  • 48
  • 245
  • 261
  • What is `self.constraints.filter{$0.firstAttribute == attribute}.first`? how do I know if will find the right constraint? –  Jun 25 '20 at 04:03
  • Note: NSLayoutConstraint.height has been renamed to NSLayoutConstraint.Attribute.height and NSLayoutConstraint.width has been renamed to NSLayoutConstraint.Attribute.width – NhlanhlaNkosi Jul 24 '22 at 21:16
14

Swift 4.2

Using auto layout and animation to increase height of button

I don't use story board. all things in code.

import UIKit

class AnimateHeightConstraintViewController: UIViewController {
    var flowHeightConstraint: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        view.addSubview(button)
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 100).isActive = true
        flowHeightConstraint = button.heightAnchor.constraint(equalToConstant: 30)
        flowHeightConstraint?.isActive = true
    }

    @objc func animateButtonTapped() {
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseOut, animations: {
            self.flowHeightConstraint?.constant = 100
            self.view.layoutIfNeeded()
        }, completion: nil)
    }

    lazy var button: UIButton = {
       let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = .green
        button.addTarget(self, action: #selector(animateButtonTapped), for: .touchUpInside)
        return button
    }()
}

and result is like this:

enter image description here

moraei
  • 1,443
  • 1
  • 16
  • 15
8

Let's call your view nibView. So, you are trying to load that view in a view controller, so first, in your view controller, you need to load it as you are doing it with:

[[NSBundle mainBundle] loadNibNamed:@"NibView" owner:self options:nil];

Then you need to tell the nibView that you don't need it to transform the autoresizing masks into constraints by doing

nibView.translatesAutoresizingMaskIntoConstraints = NO;

Then, you can add your constraints

    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:nibView
                                                                                  attribute:NSLayoutAttributeHeight
                                                                                  relatedBy:NSLayoutRelationEqual
                                                                                     toItem:nil
                                                                                  attribute:NSLayoutAttributeNotAnAttribute
                                                                                 multiplier:1.0
                                                                                   constant:yourValue];

And finally just add it to your view constraints:

[self.view addConstraint:heightConstraint];

You probably would need to add a width constraint also.

fdiaz
  • 2,600
  • 21
  • 27