Evgeniy Kleban
Evgeniy Kleban

Reputation: 6940

NSLayoutConstraint programmatically set view frame

I know there is are similar topics, but i cant figure out how to do simple thing, for example, set view frame programmatically with Auto Layout constraints. Its pretty easy to do with Storyboard, but when i try to learn about NSLayourConstraint i realise that i'm not understand topic.

Consider that we have UIView and 3 buttons. First button at top have 3 constraints (leading and trailing, and to top of a view). Other 2 buttons centered horizontally with that button and have equal widths. its pretty easy layout, i upload screenshot:

enter image description here

I have read about visual format language, but what i cant understand is - how to create constraint, for example, that relay to top (or trailing-leading)? Like following: enter image description here

Task look pretty simple but still, i did not found a way how to do that programmatically with NSLayoutConstraint. Could you please provide a solution? Thanks.

Upvotes: 0

Views: 692

Answers (1)

Rich Tolley
Rich Tolley

Reputation: 3842

Here's a solution (should go in -viewDidLoad). There are a a couple of things to note:

Firstly, VFL doesn't allow you to create all possible types of constraint. In particular, centering needs to be done with the +constraintWithItem: class method on NSLayoutConstraint.

Secondly, as noted in the comments, you could just use hardcoded left and right pad values in the horizontal VFL string to achieve the centering, but this might cause problems if you need to support different device sizes.

Thirdly, the call to -setTranslatesAutoresizingMaskIntoConstraints: is critical. Programmatic Autolayout will completely fail to work if you forget this. Also you need ensure all views are added to their superviews before setting up constraints, otherwise any constraint string referencing a superview will cause a crash

NSArray *names = @[@"button1",@"button2",@"button3"];
NSMutableDictionary *views = [[NSMutableDictionary alloc]init];

for(NSUInteger i=0;i<3;i++) {
    UIButton *b = [[UIButton alloc]init];
    NSString *name = names[i];
    [b setTitle:name forState:UIControlStateNormal];
    views[name] = b;
    [b setBackgroundColor:[UIColor redColor]];
    [b setTranslatesAutoresizingMaskIntoConstraints:false];
    [self.view addSubview:b];
}
//List of values to be used in the constraints
NSDictionary *metrics = @{
  @"buttonWidth":@150,
  @"bHeight":@50, //button height
  @"topPad":@100,
  @"vPad":@20 //vertical padding
};

//Horizontal VFL string (repeated for all rows).

for (NSString *buttonName in views.allKeys) {
    NSString *horizontalConstraintString = [NSString stringWithFormat:@"|-(>=0)-[%@(buttonWidth)]-(>=0)-|",buttonName];
    NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:horizontalConstraintString options:0 metrics:metrics views:views];
    [self.view addConstraints:horizontalConstraints];
    //Can't do centering with VFL - have to use constructor instead. You could also hardcode left and right padding in the VFL string above, but this will make it harder to deal with different screen sizes
    NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:views[buttonName] attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];

    [self.view addConstraint:centerConstraint];
}

//Vertical VFL (vertical spacing of all buttons)

NSString *verticalConstraintString = @"V:|-topPad-[button1(bHeight)]-vPad-[button2(bHeight)]-vPad-[button3(bHeight)]-(>=0)-|";
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:verticalConstraintString options:0 metrics:metrics views:views];

[self.view addConstraints:verticalConstraints];
[self.view layoutIfNeeded];

Upvotes: 1

Related Questions