AutoLayout programmatically doesn't work - ios

I want to go into the depths of auto layout and so I want to do it programmatically in order to understand it properly.I know how to use it through storyboard pretty well.Now here is what I am doing(in viewDidAppear:)
-(void)scenario2{
PGView *viewA = [[PGView alloc]initWithFrame:CGRectMake(100, 100, 100, 100) andBackgroundColor:[UIColor blackColor]];
[self.view addSubview:viewA];
PGView *viewB = [[PGView alloc]initWithFrame:CGRectMake(200, 300, 100, 100) andBackgroundColor:[UIColor yellowColor]];
[self.view addSubview:viewB];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
viewB.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewB(==100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewB)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[viewB(==100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewB)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewA(==200)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewA)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[viewA(==200)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewA)]];
//problem statement
constraint = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewB attribute:NSLayoutAttributeWidth multiplier: 3.0f constant: 0.0f];
[self.view addConstraint:constraint];
NSLog(#"%#",viewA);
NSLog(#"%#",viewB);
}
So here I have initialized two views with different colors.
I know I need to set the property. translatesAutoresizingMaskIntoConstraints to NO before applying any new constraints. So I have done that.
Now I am specifying heights and widths of both the views.So,the x_position and y_position for both the views are 0.I run the code and I get the expected result. Both views have the specified height and width at point (0,0).
Problem
Now when I am writing the statement "problem statement" to adjust the width of viewB according to the width of viewA,its not working.
Also, when I print views viewA and viewB,it prints the frames as (0,0,0,0).
Can anyone explain me this behavior?
Edit:Here are some modifications that I have made.I have checked the ambiguity of both the views using 'hasAmbiguousLayout' and its working fine now.Now what should be the next step if I want to resize viewB's width thrice to that of viewA's width?
-(void)scenario2{
PGView *viewA = [PGView new];
viewA.backgroundColor = [UIColor yellowColor];
[self.view addSubview:viewA];
PGView *viewB = [PGView new];
viewB.backgroundColor = [UIColor blackColor];
[self.view addSubview:viewB];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
viewB.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[viewB(==100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewB)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[viewB(==100)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewB)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[viewA(==200)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewA)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[viewA(==200)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(viewA)]];
NSLog(#"%d",viewA.hasAmbiguousLayout);
NSLog(#"%d",viewB.hasAmbiguousLayout);
NSLog(#"%#",viewA);
NSLog(#"%#",viewB);
}

The problem is that you've explicitly set width constraints for A and B to be 200 and 100, respectively. So the additional constraint that specifies that one should have a width of three times the other one is unsatisfiable when combined with the other constraints.
Furthermore, the constraints are ambiguous, as you've defined width and height constraints, but haven't defined where the two frames should be. Yes, you've defined the frame for these two views, but that is ignored when you apply the constraints. You probably shouldn't define the frame values at all, as it's misleading and confusing. But you should set, for example, the leading and top constraints, or specify those in the VFL, or add centerX and centerY constraints. You just want some constraints to dictate where the views should be placed and there's lots of ways to specify that.
And the reason that you're not seeing reasonable frame values at the end is that you've defined the constraints, but they haven't been applied yet. You could examine them in viewDidLayoutSubviews, if you wanted.
You might do something like:
- (void)scenario2 {
PGView *viewA = [PGView new];
viewA.backgroundColor = [UIColor yellowColor];
[self.view addSubview:viewA];
PGView *viewB = [PGView new];
viewB.backgroundColor = [UIColor blackColor];
[self.view addSubview:viewB];
viewA.translatesAutoresizingMaskIntoConstraints = NO;
viewB.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(viewA, viewB);
// note, I added `|-100-` to say it's 100 points from the top (and 100 tall)
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[viewB(==100)]" options:0 metrics:nil views:views]];
// likewise, to say 200 points from the left (and 100 wide)
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-200-[viewB(==100)]" options:0 metrics:nil views:views]];
// again, 100 from the top (as well as 200 tall)
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[viewA(==200)]" options:0 metrics:nil views:views]];
// and 100 from the left
// but I've removed width definition, as we'll use your multiplier constraint below
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-100-[viewA]" options:0 metrics:nil views:views]];
// now that we've removed the width constraint from the above,
// this should now work fine
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:viewA attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewB attribute:NSLayoutAttributeWidth multiplier: 3.0f constant: 0.0f];
[self.view addConstraint:constraint];
// no point in doing this, because constraints haven't yet been applied
//
// NSLog(#"%#",viewA);
// NSLog(#"%#",viewB);
}
// if you want to see where they are, you could do that here:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
for (UIView *view in self.view.subviews) {
NSLog(#"%#", view);
}
NSLog(#"-----");
}

Related

need help in autolayout top layout guide, UIscrollView, UIImageView

I am working on a top sticked streach header.
My setup:
UIScrollView
-UIView(a container)
-UIImageView
-UIView(subContent view)
What i want:
Stick UIImageView to top layout guide.
UIImageView height is 100 - 250. That is UIImageView height is initialise with 250, but can not be less than 100.
0 top space in between UIImageView and subContentView.
So when user scroll towards bottom, UIImageView will gradually minimise and will no more minimise as soon as it's height reaches 100(minimum height).
BUT subContentView must not overlap my UIImageView and be able to scroll till end (accurate content size/ offset)
What i am able to do is:
i am able to stick UIImageView to top based on layout guide.
I am also able to acheive height range of UIImageView on scroll top or bottom (increase and decrease height).
BUT i am not able to stop UIImageView from being overlapping by my subContentView.
That is when i scroll to bottom my UIImageView remains stick to top and gradually decreases it's size, at the moment it reaches to height 100,(i am still scrolling bottom) my subContentView starts overlapping my UIImageVIew.
How ever it makes sense as i have no set constraints from top layout guide to subcontent view.
some thing like:
format = #"V:|[topGuide]-**some dynamic value based on UIImageViewHeight**-[subContentView]";
How do i achieve this.
Please help me i am working on it from a day, and finally i decided to ask.
my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.title = #"Programmatically";
NSMutableDictionary* views = [NSMutableDictionary new];
views[#"super"] = self.view;
views[#"topGuide"] = self.topLayoutGuide; // The bottom of the navigation bar, if a navigation bar is visible. The bottom of the status bar, if only a status bar is visible .. etc...
//Create the ScrollView
UIScrollView* scrollView = [UIScrollView new];
scrollView.delegate = self;
scrollView.translatesAutoresizingMaskIntoConstraints = NO; //we are using auto layout
self.automaticallyAdjustsScrollViewInsets = NO;
[self.view addSubview:scrollView];
views[#"scrollView"] = scrollView;
//Create the scrollview contentview
UIView* contentView = [UIView new];
[contentView setBackgroundColor:[UIColor yellowColor]];
contentView.translatesAutoresizingMaskIntoConstraints = NO; //we are using auto layout
[scrollView addSubview:contentView];
views[#"contentView"] = contentView;
//Add the image view and other addtional views to the content view
topImageView = [[WAPlayerDetailHeader alloc] initWithFrame:CGRectZero andImage:nil];
topImageView.translatesAutoresizingMaskIntoConstraints = NO; //we are using auto layout
topImageView.contentMode = UIViewContentModeScaleAspectFill;
topImageView.clipsToBounds = YES;
[contentView addSubview:topImageView];
views[#"topImageView"] = topImageView;
//Add other content to the scrollable view
UIView* subContentView = [UIView new];
[subContentView setBackgroundColor:[UIColor redColor]];
subContentView.translatesAutoresizingMaskIntoConstraints = NO; //we are using auto layout
[contentView addSubview:subContentView];
views[#"subContentView"] = subContentView;
//Now Let's do the layout
NSArray* constraints;
NSString* format;
NSDictionary* metrics = #{#"imageHeight" : #250.0};
//======== ScrollView should take all available space ========
format = #"|-0-[scrollView]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[self.view addConstraints:constraints];
format = #"V:[topGuide]-0-[scrollView]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[self.view addConstraints:constraints];
//======== ScrollView Content should tie to all four edges of the scrollview ========
format = #"|-0-[contentView]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[scrollView addConstraints:constraints];
format = #"V:|-0-[contentView]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[scrollView addConstraints:constraints];
// ========== Layout the image horizontally
format = #"|-0-[topImageView(==super)]-0-|"; // with trick to force content width
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[self.view addConstraints:constraints];
// ========== Put the sub view height, and leave room for image
format = #"|-0-[subContentView]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[scrollView addConstraints:constraints];
// we leave some space between the top for the image view
format = #"V:|-imageHeight-[subContentView(1000)]-0-|"; /*the view height is set to 700 for the example in order to have enough content */
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:metrics views:views];
[scrollView addConstraints:constraints];
// Start of the magic
format = #"V:[topImageView]-0-[subContentView]"; // image view bottom is subcontent top
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[contentView addConstraints:constraints];
// image view top is the top layout
format = #"V:|[topGuide]-0-[topImageView(>=100)]";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[self.view addConstraints:constraints];
//Optional stuff, Add The A view
aView = [[WAPlayerDetailHeader alloc] initWithFrame:CGRectZero andImage:nil];
[aView setAlpha:1.0];
aView.translatesAutoresizingMaskIntoConstraints = NO; //we are using auto layout
aView.backgroundColor = [UIColor colorWithRed:0.79 green:0.9 blue:0.69 alpha:1];
[subContentView addSubview:aView];
views[#"aView"] = aView;
format = #"|[aView]|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[subContentView addConstraints:constraints];
format = #"V:|-[aView(127)]-5-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[subContentView addConstraints:constraints];
}
this is what happening video demo
i am following this code from this location
i have upload on git download and you can see

add subviews generated by autoayout to a parent view

This is my very first program in autolayout.
Basic problem: i am not able to add subviews(a uibutton and a uilabel) to a superview(a containerview).Subviews are just out of bond of superview or say not clipped.
I have added commented in detail to be better understanding of code.
What i want:
i dont care whereever containerview is but i want both subviews to be add in containerview with 0 padding from all sides.
- (void)viewDidLoad {
[super viewDidLoad];
**//create a uibutton with dynamic text(MAX_WIDTH=500, height = 60) and uilabel of fixed size(60, 60).Done
//create pin of fixed 2 pixes between UIButton and UILabel.Done
//put above created views in container view, it will max to 562 width and fix 60 height, so UIButton and UIlabel should fill container view with no top, bottom, left and right.Fail**
//this will be containing my button and my label
UIView *superview = self.view;
UIView *containerView = [UIView new];
[containerView setBackgroundColor:[UIColor blackColor]];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
[superview addSubview:containerView];
//this will be containing my button and my label
UILabel *mylabel = [[UILabel alloc]init];
[mylabel setBackgroundColor:[UIColor redColor]];
[mylabel setTranslatesAutoresizingMaskIntoConstraints:NO];
mylabel.text = #"MyLabel";
UIButton *mybutton = [UIButton
buttonWithType:UIButtonTypeRoundedRect];
[mybutton setTitle:#"My Button ye ye yey yeyeyye yeyey"
forState:UIControlStateNormal];
[mybutton setTranslatesAutoresizingMaskIntoConstraints:NO];
[mybutton setBackgroundColor:[UIColor greenColor]];
[containerView addSubview:mylabel];
[containerView addSubview:mybutton];
NSDictionary * views = NSDictionaryOfVariableBindings(mybutton,mylabel);
//create pin of fixed 2 pixes between UIButton and UILabel.Done
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mylabel(==60)]" options:0 metrics:nil views:views];
[containerView addConstraints:horizontalConstraintsforbuttons];
[containerView addConstraints:heightConstraintforbutton];
[containerView addConstraints:heightConstraintforLabel];
//container view specific constraints//**it must be ideally <=562, but then this container view disappears, please hep to fix**
NSArray *widthConstraintForConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(==560)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
NSArray *heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[containerView(==60)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
[superview addConstraints:widthConstraintForConstraint];
[superview addConstraints:heightConstraint];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0]];
}
Any suggestion? :)
The VFL for each of your subviews is missing a relationship with the parent view. Autolayout is assuming your constraints should be in relation to the top-level view — what you've defined as self.view.
Here's where your problem is.
NSDictionary * views = NSDictionaryOfVariableBindings(mybutton,mylabel);
//create pin of fixed 2 pixes between UIButton and UILabel.Done
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat
First, add your containerView to that dictionary so you can refer to it in VFL:
NSDictionary *views = NSDictionaryOfVariableBindings(mybutton,
mylabel,
containerView);
Then in your VFL, use the pipe operator (|) to tell autolayout to place your subviews in relation to their immediate parent.
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]|" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[mylabel(==60)]|" options:0 metrics:nil views:views];
I'm not entirely sure what you're trying to do with the horizontal placement of those views, but this should get you back on track. I recommend reading this post on VFL, too.
Edit
I sort of see what you're trying to do now. First, base your values at 1x when working with VFL and autolayout. As an example, a width of 560 is larger than the largest possible iPhone screen:
NSArray *widthConstraintForConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(==560)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
Let's pretend you just wanted containerView to match the width of the device. That would look like this:
#"H:|[containerView]|"
Those pipe operators outside the containerView are saying that you want the leading (left side) and trailing (right side) space of containerView to be flush with the superview.
Alternatively, let's say you wanted your view to be slightly smaller than the width of the device (560/2).
#"H:[containerView(==280)]"
You're already horizontally centering containerView elsewhere, so it'll appear in the center of it's superview.
Let's then assume you want your red label to (actually) have a width of 60 and your green button to have a width less than or equal to 250 (500/2). That would look like this:
#"H:|[mybutton(<=250)]-2-[mylabel(==60)]|"
Since these are subviews of containerView (and we told that to autolayout earlier), the pipe operators are saying you want
the leading space (left side) of mybutton to be flush with containerView.
the trailing space (right side) of mylabel to be flush with containerView.
Since mylabel has a width of 60, mybutton will be narrower (thanks to <=) to satisfy constraints, depending on the width of containerView.

What auto layout constraints lets a subview to compress to 0 size when the parent's size decreases?

Trying to figure out what constraints do I need to add to a view-subview layout hierarchy that would allow the subview to auto resize when the parent view's size changes. Not using interface builder intentionally, translatesAutoresizingMaskIntoConstraints is set to NO for all participating views.
Below is a code snippet that tries to describe the situation:
UIView *canvas = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
container.translatesAutoresizingMaskIntoConstraints = NO;
[canvas addSubview:container];
NSNumber *height = #(50);
NSNumber *margin = #(10);
NSDictionary *metrics = NSDictionaryOfVariableBindings(height, margin);
NSDictionary *views = NSDictionaryOfVariableBindings(container);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(margin)-[container(height)]" options:0 metrics:metrics views:views];
[canvas addConstraints:constraints];
NSLayoutConstraint *containerHeightConstraint = [constraints objectAtIndex:1]; // Know this is unsafe and shouldn't use the visual format in this case, but visual format serves SO purposes better
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-(margin)-[container]-(margin)-|" options:0 metrics:nil views:views];
[canvas addConstraints:constraints];
UIView *subView = [[UIView alloc] initWithFrame:CGRectZero];
subView.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:subView];
margin = #(5);
metrics = NSDictionaryOfVariableBindings(margin);
views = NSDictionaryOfVariableBindings(subView);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(margin)-[subView]-(margin)-|" options:0 metrics:metrics views:views];
[container addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-(margin)-[subView]-(margin)-|" options:0 metrics:metrics views:views];
[container addConstraints:constraints];
[container layoutIfNeeded]; // Layout before animation
containerHeightConstraint.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[container layoutIfNeeded]; // Animate container to 0 height
} completion:nil];
The question is what constraints do I need to add that would mimic the autoresize mask constraints that Interface Builder creates and would allow subView in the above example to shrink to 0 height as container shrinks.
If you could also explain the mechanics of such constraints that would help me understand Auto Layout better.
All you need is to set the height and width constant constrain to your subview when the container resizes.

Center views using *only* visual format?

I'm trying to center 2 views with different height vertically.
UILabel *view1 = [[UILabel alloc] init];
view1.backgroundColor = [UIColor redColor];
view1.text = #"view1\nline2";
view1.textAlignment = NSTextAlignmentCenter;
view1.numberOfLines = 0;
UILabel *view2 = [[UILabel alloc] init];
view2.backgroundColor = [UIColor greenColor];
view2.text = #"view2\nline2";
view2.textAlignment = NSTextAlignmentCenter;
view2.numberOfLines = 0;
[self.view addSubview:view1];
[self.view addSubview:view2];
NSDictionary *views = #{#"view1": view1, #"view2" : view2};
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
for (UIView *view in views.allValues) {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view1]-[view2(==view1)]-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=20)-[view1(200)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=20)-[view2(100)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
This manages to make the centers of both aligned vertically, but they are at the bottom of the superview!
I want to center vertically not only relative to each other, also in the superview.
I added a constraint like this and it works:
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0]];
But I want to know, is this possible to achieve using only with visual format?
Yes, it's possible, but only by adding spacer views. So if you create a couple of views, lets call them spacer1 and spacer2, you can center view1 with this string,
#"V:|[spacer1][view1][spacer2(==spacer1)]|"
This assumes that view1 has a fixed height. I wouldn't recommend doing it this way though, I would just use the code you show at the bottom of your post.
What about calculating the Y in "metrics" ?
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-centerY-[view1(200)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:#{#"centerY": #(CGRectGetHeight(self.view.frame)/2.0 - 100)}
views:views]];

Using NSLayoutConstraint Visual Format to set width of items

I'm trying to get a hang of NSLayoutConstraints visual format and I am having a little trouble.
What I want is basically a simple UIView with two labels, one on the left, and one on the right, with heights equal to the height of the container, and the widths equal to 1/2 of the container width. The origin of the right label is aligned to the trailing of the left label.
| LEFT LABEL - RIGHT LABEL|
Anyway, here is my attempt:
self.leftLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.rightLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = #{ #"leftLabel" : self.leftLabel,
#"rightLabel" : self.rightLabel };
NSDictionary *metrics = #{ #"width" : #(CGRectGetWidth(self.frame) / 2) };
NSArray *constraints;
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[leftLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[rightLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[leftLabel(width)]" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-width-[rightLabel(width)]" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
Unfortunately, the width of each label remains 0 after this executes. Any suggestions?
UPDATE
Here's the whole class:
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
self.leftLabel = [UILabel new];
self.rightLabel = [UILabel new];
self.loadingView = [[DDLoadingView alloc] init];
self.rightLabel.backgroundColor = [UIColor redColor];
self.leftLabel.backgroundColor = [UIColor redColor];
self.backgroundColor = [UIColor blackColor];
[self addSubview:self.leftLabel];
[self addSubview:self.rightLabel];
[self configureLayout];
}
return self;
}
- (void)configureLayout
{
self.leftLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.rightLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = #{ #"leftLabel" : self.leftLabel,
#"rightLabel" : self.rightLabel,
#"loadingView" : self.loadingView };
NSDictionary *metrics = #{ #"width" : #(CGRectGetWidth(self.frame) / 2) };
NSArray *constraints;
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[leftLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[rightLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[leftLabel]-[rightLabel(==leftLabel)]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
}
You need to set the constraints to use the widths of the other views:
NSArray *constraints;
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[leftLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[rightLabel]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[leftLabel]-[rightLabel(==leftLabel)]-|" options:0 metrics:metrics views:views];
[self addConstraints:constraints];
The last constraint is saying make those two views the same width while pinning them to the left/right egdes of the superview. See the Equal Widths section of the docs
As discussed in the comments, you need to also make sure that the frame of the superview can accommodate the views themselves. |-[ will use the default insets of 20, using |-2- will give insets of 2. If the intrinsic height of the labels is more than the views height minus these insets your view isn't going to show. So you either need to reduce the insets or increase the height of the container.
If you want more information on AutoLayout I recommend the following series of blog posts by #jrturton - see the top of the article for other posts in the series. He's also got a great category on UIView to make adding constraints easier, I use this daily!

Resources