65
votes

Apple's documentation on creating Auto Layout constraints between a view and one of the layout guides only shows an example using VFL.

Is there any way to create these constraints programmatically without VFL (using NSLayoutConstraint's other API or similar)?

(Note: I'm specifically asking about doing this in code, not in Interface Builder. And I don't want the calculated length of the guide set as a static constant on a constraint, I want a constraint where changes to the layout guide length would automatically cause the constrained view to adjust position.)

4
What is VFL? The link is broken...Nicolas Miari

4 Answers

93
votes

For a UIButton that you want to place 20 points below the UIViewController.topLayoutGuide you create the NSLayoutConstraint like so:

[NSLayoutConstraint constraintWithItem:self.button
                             attribute:NSLayoutAttributeTop
                             relatedBy:NSLayoutRelationEqual
                                toItem:self.topLayoutGuide
                             attribute:NSLayoutAttributeBottom
                            multiplier:1.0
                              constant:20.0];

With iOS 9 you can also create the NSLayoutConstraint this way:

[self.button.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor
                                      constant:20.0];
6
votes

To supplement @JamieMcDaniel's answer, the Swift + iOS9 version would be:

self.button.topAnchor
    .constraintEqualToAnchor( self.topLayoutGuide.bottomAnchor ).active = true

Don't forget the .active = true part as otherwise the constraint doesn't kick in automatically.

3
votes

This is a gist I've created, you are supposed to embed all your subviews into a tank view(container view) added into a xib, it removes tank view-superview xib constraints and adds an upper constraints to topLayoutGuide giving an iOS6 look. It could be interesting for what you want to achieve.

//This should be added before the layout of the view
- (void) adaptToTopLayoutGuide {
    //Check if we can get the top layoutguide
    if (![self respondsToSelector:@selector(topLayoutGuide)]) {
        return;
    }
    //tankView is a contaner view
    NSArray * array = [self.tankView referencingConstraintsInSuperviews]; //<--For this method get the Autolayout Demistified Book Sample made by Erica Sadun
    [self.view removeConstraints:array];
    NSArray * constraintsVertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topLayoutGuide]-0-[tankView]|" options:0 metrics:nil views:@{@"tankView": self.tankView, @"topLayoutGuide":self.topLayoutGuide}];
    [self.view addConstraints:constraintsVertical];
    NSArray * constraintsHorizontal = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tankView]|" options:0 metrics:nil views:@{@"tankView": self.tankView}];
    [self.view addConstraints:constraintsHorizontal];

}
3
votes

Just an addition to @Jamie McDaniel, in case it's not immediately obvious, you need to add the constraint that he suggests to create:

NSLayoutConstraint *buttonTopConstraint = [NSLayoutConstraint constraintWithItem:self.button
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:self.topLayoutGuide
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:20.0];
[self.view addConstraint:buttonTopConstraint];