iOS8 Auto layout programmatically pin to relative layout margin

Legoless picture Legoless · Oct 2, 2014 · Viewed 16.8k times · Source

I have an UI element (UISwitch actually, but does not matter really) that has both leading and trailing space pinned to superview in Interface Builder. The constraint looks like this in Xcode 6:

Constraint pin

The constraint for leading space is the same effectively. The value of constraint is 42.0 points.

This is exactly what I want, because for different devices I can change layoutMargins property on UIView and the constraints will work correctly, to increase margin between views.

Now I want to add another view in code that would also have both leading and trailing space pinned to it's superview margin, so the same layoutMargins set to superview will work.

I pinned the view using visual format language with the following syntax:

NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-42.0-[separatorView]-42.0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self.contentView, separatorView)];

[self.contentView addConstraints:constraints];
[self.contentView setNeedsUpdateConstraints];

This works, but layoutMargins property has no effect using this constraint, so it is obviously not pinned to margin, but directly to superview.

So my question is:

How to pin UI element spaces to margin in code using visual format language? Or if not possible, how to pin with constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: API?

Answer

algal picture algal · Oct 10, 2014

In iOS8, the visual format language has been updated so that "|-" or "-|" will default to using a spacing defined by the superview's layoutMargins property.

So the answer using visual format language is as follows:

// programmatically set the layoutMargins, only if
// you want non-default values and they are not already set in IB!
self.contentView.layoutMargins = UIEdgeInsetsMake(0,42,0,42); // set left and right margins to 42

// assume: seperatorView is already a subview of self.contentView

// separatorView will use the constraints because we write "-" between it and the superview edge
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[separatorView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(separatorView)];
[self.contentView addConstraints:constraints];

If you want to refer to the layout margins when creating the constraints via the direct API, then you use the new iOS8 only layout attributes:

NSMutableArray * constraints = [NSMutableArray array]; 
[constraints addObject:[NSLayoutConstraint constraintWithItem:self.contentView 
     attribute:NSLayoutAttributeLeftMargin 
     relatedBy:NSLayoutRelationEqual 
     toItem:separatorView
     attribute:NSLayoutAttributeLeft
     multiplier:1.0
     constant:0]];
[constraints addObject:[NSLayoutConstraint constraintWithItem:self.contentView 
     attribute:NSLayoutAttributeRightMargin 
     relatedBy:NSLayoutRelationEqual 
     toItem:separatorView
     attribute:NSLayoutAttributeRight
     multiplier:1.0
     constant:0]];
[self.contentView addConstraints:constraints];