3

In Visual Formatting Language is there a way to keep two elements in the same position on one axis?

For example I have a UILabel and a UITextField that I want alongside each other horizontally. When laying out the vertical constraints is there anyway of specifying this?

Something like this would be ideal:

|-40-([label][field])-10-[otherStuff]

...where [label] and [field] both have the same Y position, but are 40 points down from the top of the superview and 10 points beneath them is [otherStuff].

Is this possible?

Or should I nest the [label] and [field] in their own UIView and then lay that out?

Jimmery
  • 9,783
  • 25
  • 83
  • 157

3 Answers3

2

The visual format language does not support this in and of itself. First, the horizontal layout and the vertical layout have to be done in two separate strings. However +[NSLayoutConstraint constraintsWithVisualFormat:options:metrics:views:] takes options which can include alignment.

So, you could use:

NSDictionary* views = NSDictionaryOfVariableBindings(field, label);
NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-40-[label]-10-[otherStuff]" options:0 metrics:nil views:views];
[field.superview addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"[label][field]" options:NSLayoutFormatAlignAllBaseline metrics:nil views:views];
[field.superview addConstraints:constraints];

In the second case, the NSLayoutFormatAlignAllBaseline makes it so that, not only does field trail label, but they are in the same vertical position (based on their baseline, which is probably better than aligning their center, top, or bottom).

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Although all the answers here are suitable, i ended up using the `NSLayoutFormatAlignAllBaseline` method. Many thanks! – Jimmery Feb 12 '15 at 10:48
  • What should I do if I want label to have a specific height? should I [label(40)] or [label(==40)] or something else? – mfaani Jan 12 '17 at 21:12
1

Think of it this way: if you can do it with Autolayout in Interface Builder, you can do it using Visual Format Language as well.

However, for the use case you suggested, you'll have to describe the constraints in multiple statements:

  1. Set the horizontal sameness of field and label
  2. Set the vertical space of 40 px from the top of the superview to the field and the 10 px to otherStuff

All that's required overall is that for each subview, all 4 of the necessary placement values (x, y, width and height) are defined unambiguously.

My general approach towards Autolayout by code is to write a custom library with methods that do the same things that individual constraints in Interface Builder do. Here are some sample method signatures:

  1. +(void)addFixedHeightConstraintToView:(UIView*)view height:(CGFloat)height;
  2. +(void)addTopMarginFromSuperviewConstraintToView:(UIView*)view topMargin:(CGFloat)topMargin;
  3. +(void)addHorizontalSpaceConstraintFromView:(UIView*)fromView toView:(UIView*)toView horizontalSpace:(CGFloat)hSpace;

These methods are all defined with very simple and understandable VFL. Once I have these, I can easily solve the use case you described. Here's some sample code:

[CustomAutoLayout addTopMarginFromSuperviewConstraintToView:field topMargin:40];
[CustomAutoLayout addTopMarginFromSuperviewConstraintToView:label topMargin:40];
[CustomAutoLayout addHorizontalSpaceConstraintFromView:field toView:label horizontalSpace:0];
[CustomAutoLayout addVerticalSpaceConstrantFromView:field toView:otherStuff verticalSpace:10];
Vinod Vishwanath
  • 5,821
  • 2
  • 26
  • 40
1

The only way I know of would be two use 2 visual format statement like this:

|-40-[label]-10-[otherStuff]
|-40-[field]

Unfortunately, that means duplicating part of the first statement.

Nesting into another view is a solution.

You can also add constraints in code without using visual format language:

NSLayoutConstraint* fieldLeft = [NSLayoutConstraint constraintWithItem:field
           attribute:NSLayoutConstraintAttributeLeading
           relatedBy:NSLayoutRelationEqual 
           toItem:label 
           multiplier:1 constant:0];
[field.superview addConstraint:fieldLeft];
joe77
  • 211
  • 2
  • 5