You know that scene from lord of the rings where bilbo is chillin with Gandalf ,blowing smoke rings. All of a sudden this guy just blows a friggin ship from out of nowhere, bilbo just hangs and stares.
Thats the exact same feeling when looking at a friggin VFL sample from ANywhere. So battling with this for a while I decided to write a SUPER high level dummy guide to constraints.
So first things first, why is your app crashing when you adding crap loads of constraints to your view
The view your setting constraints to ain't got no parent.
Your naming convention or dictionary is wrong. VFL is a idiot so if you do know specify the dictionary well it crashes. we will get to this
Your constraints are soooo in conflict that you just killed xcode.
Remember So-cratz - "The only true wisdom consists in knowing that you know nothing".
So point one, if you set constraints to something that has no superview it ain't going to work. When you think about it, it kind of makes sense. Xcode doesn't magically know what this UIlabel is going to be a child of a view.
Also whenever you are even thinking about using a VFL for views. You have to implement this guy
setTranslatesAutoresizingMaskIntoConstraints
By defualt autoresizing masks on a view uses constraints to determine view's position. So if you are setting constraints on a view manually you have to set that to NO. Then you will have no conflicts with default constraints.
Try not to use initWithFrame at all, replace that guy with new.
// no no no no UIView * newView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; // yeah yeah yeah UIView * newViewAuto = [UIView new]; [newViewAuto setTranslatesAutoresizingMaskIntoConstraints:NO];
Warning bells might be ringing right now due to no frames being set. Remember that your view will autoresize according to intrinsic content size. Basically it will be as big as whatever you shove in it. Also you are going to set constraints to that view anyways when you add it to its superview. This in turn will set the size of the view.
So if your view had no set widths or heights, but your constraints set its edges to that of the superview. This will stretch the view to fullscreen.
Ok one of the stupidly annoying things about VFL is that its kinda dumb. You are essentially passing it a string with the names of your UIElements. In this string you are telling it padding values, width and height values and whether its aligned vertical or horizontal. THATS IT
If you want to align things up and Down Use a V
[outletname] You declare references to outlets by shoving them in brackets
Means that a background box is going to be set to edges of superview VERTICALLY, not horizontally.
OK so you got vertical down, now you want to mess with widths. To do that Use H
So this guy is basically setting the backgroundBox to the edges of the superview's horizontal components
So if you combined the two
That will stretch the background box to the edges of the view, so full screen. Notice here that we did not set the frame values EVER.
Ok so point 2 was the basics but pretty useless in real life development. Lets try to use a nicer example, one that you will actually benefit from.
We are doing to make a traffic light
In autolayout it looks like this
UIView * newViewAuto1 = [UIView new]; [newViewAuto1 setBackgroundColor:[UIColor redColor]]; [newViewAuto1 setTranslatesAutoresizingMaskIntoConstraints:NO]; UIView * newViewAuto2 = [UIView new]; [newViewAuto2 setBackgroundColor:[UIColor yellowColor]]; [newViewAuto2 setTranslatesAutoresizingMaskIntoConstraints:NO]; UIView * newViewAuto3 = [UIView new]; [newViewAuto3 setBackgroundColor:[UIColor greenColor]]; [newViewAuto3 setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.view addSubview:newViewAuto1]; [self.view addSubview:newViewAuto2]; [self.view addSubview:newViewAuto3]; NSDictionary * bindings = NSDictionaryOfVariableBindings(newViewAuto1, newViewAuto2,newViewAuto3); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newViewAuto1]|" options:0 metrics:nil views:bindings]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newViewAuto2]|" options:0 metrics:nil views:bindings]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newViewAuto3]|" options:0 metrics:nil views:bindings]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newViewAuto1][newViewAuto2(==newViewAuto1)][newViewAuto3(==newViewAuto1)]|" options:0 metrics:nil views:bindings]];
So what are we doing, well first off we create the three views and set the colours.
Next we need to create a dictionary which VFL can reference the views. The guys at apple make a nice helper method that does this for us.(although you can also just create a nsdictionary manually and set the key/values yourself)
NSDictionary * bindings = NSDictionaryOfVariableBindings(newViewAuto1, newViewAuto2,newViewAuto3);
This method will make a dictionary with the value name and key set to the name of the outlet.
So "newViewAuto1"= <newViewAuto1 obj>
This is how VFL references the outlet, So in this line
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newViewAuto1]|"
We are setting the horizontal leading and trailing of newViewAuto to be pinned to the same of the superview. Not that the string references "newViewAuto1", which obviously will be referred in the dictionary. If you get the naming wrong in VFL it will crash so make sure you get the names you reference correct.
Now lets do the vertical alignment to get them all in a nice row format.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newViewAuto1][newViewAuto2(==newViewAuto1)][newViewAuto3(==newViewAuto1)]|" options:0 metrics:nil views:bindings]];
So this is what is going on
Using V so we are aligning everything vertically
We are putting the views next to each other with no padding values. So they will naturally be positioned right next to each other.
Finally inside the [] brackets we setting a equals constraint. By setting (==newViewAuto1) because its in a vertical constraint, this will basically set the height of the view to be equal the other view. So [newViewAuto2(==newViewAuto1)]. Means newViewAuto2's height will be the same as newViewAuto1's. This is set to newViewAuto3 aswell. This is how we have equal height distribution.
You can also use other relations such as == ,<= ,>=.
Set up constraints slowly and one at a time, try not setting constraints everywhere at once cause it will be a pain to debug.
Also because you do not have to specify view sizes now because of intrinsic content size. It is good practice to added and set constraints into containers. Then position the container in relation to the whole superview. So in 1 case say you have 4 labels with padding aligned. You can just added them to a container view then position that view central to the super. This will in turn position all the views.