How do you make a UIButton's vertical distance equivalent to a UIButton's horizontal distance in AutoLayout.
Using Visual Format Language, I can currently make all UIButtons have the equivalent height or width, but I don't know how to specify those to be the same.
I also do not want this distance to be predefined which I know is possible through setting metrics.
You can't use vfl for this.
You have to use the method...
AddConstraintForItem:attribute:relation:toItem:attribute:multiplier:constant.
Or something like that anyway.
Both items can be the same and the two attributes set one to width and one to height.
Figured out a solution to this. You can't use Visual Format Language, but rather need to use NSLayoutConstraint constraintWithItem
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:forwardButton attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:forwardButton attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];
[self.view addConstraint:constraint];
I have a tableViewCell with a label inside. Lets say I dont want to create an IBOutlet in this case. I want to add a constraint to the label based on a condition so that label moves 10pixels down if the condition is true. To be more precise, I want to achieve the constraint in the image below by code.
I have tried
NSLayoutConstraint *myConstraint = [NSLayoutConstraint constraintWithItem:amountLabel attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:cell.superview attribute:NSLayoutAttributeTop
multiplier:1.0 constant:+10];
[amountLabel addConstraint:myConstraint];
but to no effect. Any help would be appreciated. :)
I think you would have to add that constraint to the superview to get it to work (always add constraints to the topmost view, or to the nearest common ancestor view).
I've found that working with constraints in code is WAY easier using a category library. My favorite is the one made by Richard Turton: https://github.com/jrturton/UIView-Autolayout
I think you will find adding constraints is much more intuitive using this.
Example
[amountLabel pinEdge:NSLayoutAttributeTop toEdge:NSLayoutAttributeTop ofView:superView inset:10];
I’m beginning to implement AutoLayout, and I’m trying to piece together how I can hook up one of my custom UITableViewCell classes properly. It usually has two UILabel objects, one on top of another, much like the standard subtitle class. I’d like these two labels to be centred in the cell, regardless of the cell’s height, with a given padding between the two.
I assume I can do this by doing something like add the heights of the two labels, add the padding, then subtract that from the height of the cell, and divide by two. However, I’m curious semantically whether this is correct, since I’d be constraining them from the top and bottom of the cell, rather than from each other. Am I missing a trick here?
Secondly, there’s sometimes a third label stacked in there too, so three on top of one another. In that instance, I’d need two sets of the padding, etc., but the question becomes even more relevant: shouldn’t I be constraining them to each other, rather than to the top and bottom of the cell?
So, question is more of a semantic one: if I want to constrain multiple elements vertically inside a parent view, is there a smarter way to do this than the method I suggested above?
(I’m currently implementing AutoLayout entirely in code (using Masonry) since this cell in question has no XIB and isn’t in a Storyboard).
The correct approach is to use a container view, which derives its height from its subviews. The container view is then pinned to the centre of the cell - you wouldn't have any constraints linking the container to the top and bottom edges of the cell.
Within the container, the vertical constraints would be |[label1]-[label2]|, which would make the container the height of the two labels plus the space, and the centre of the container view would be between the two labels.
If you added three labels, it would be |[label1]-[label2]-[label3]| and the centre of the container would be in the centre of the middle label.
In each case the centre of the container would be at the centre of the cell, and you don't need to calculate anything.
Using Interface Builder, I'm able to vertically center multiple UIViews using the following steps. My use case is that I want these multiple UIViews to behave like a group as much as possible but I don't want to introduce any further views in order to achieve that.
Firstly, I select what I consider to be my default Form Factor using the button along the bottom (Form Factor being 3.5 inch or 4 inch retina). Then, I position the buttons in the center of the view by dragging one at a time, and I use the auto-snapping with blue guidelines to help with this.
Once the buttons are in place, I select them one at a time to apply their constraints. Click on a button, then select the Align Constraints menu from the floating buttons at the bottom right in Interface Builder (I see a set of 4, and the Align is the one on the left, next to the Form Factor button).
In this menu, check the Vertical Center In Container checkbox, then click to open the dropdown menu next to the value, which is probably 0. From that list, choose Use Current Canvas Value. Then, hit the Add Constraints button (possibly labelled Add 1 Constraint).
As you do this, you'll probably know it's working if you see the values next to Vertical Center In Container becoming set to Y-offset values you might guess are correct by glancing back to your arrangement of UIViews.
Apologies if you don't want to use Interface Builder, but I had trouble achieving this myself and wanted to mention these steps. You could always use IB as a one-off, and programmatically log out the constraints in a test, then take them and apply in your code-only solution.
Assuming you have label1, label2, label3, and cell
// Center the labels
[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
toItem:cell.contentView attribute:NSLayoutAttributeCenterX
multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
toItem:cell.contentView attribute:NSLayoutAttributeCenterX
multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
toItem:cell.contentView attribute:NSLayoutAttributeCenterX
multiplier:1.0 constant:0]
// Vertical alignment/spacing of 8 between each label
[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:cell.contentView attribute:NSLayoutAttributeTop
multiplier:1.0 constant:0]
[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:label1 attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:8]
[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:label2 attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:8]
You can nest everything inside a for() loop I guess so you won't have to repeat the constraints
No code. Just edit in the storyboard.
I'm hoping someone can help me out with this problem. I have a very tall view with three different content section. For the sake of simplicity I will say that the UIView in Interface Builder is 1000px tall, each section is 300px tall, and there is a 50px margin between sections. Each section can be visible or hidden depending on the underlying data. So you could have 1,2, and 3 visible. Or 1 and 3. Or 1 and 2. Or just 3. Etc.....
In Interface Builder I set up all the constraints except for the vertical constraints that link the sections together and the constraints that link the sections to the top and bottom of the parent view. Those are all added at run time depending what sections are showing.
//sample code
//add constraint from top of view to first section
NSLayoutConstraint* topConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:section1 attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[self.view addConstraint:topConstraint];
//add constraint between sections
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem:section1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:section2 attribute:NSLayoutAttributeTop multiplier:1.0 constant:50.0];
[self.view addConstraint:constraint];
//add constraint from bottom section to view
NSLayoutConstraint* bottomConstraint = [NSLayoutConstraint constraintWithItem:section2 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.view addConstraint:bottomConstraint];
When all three are visible, everything is fine. However, when I hide one, one or more of the elements on the screen is vertically stretched so that the content size now matches the size 1000px size of the parent view as it is drawn in Interface Builder. At least that's what I think is going on. I would instead expect the parent view to shrink to the size of the content.
Example: sections (300px each) 1 and 2 are showing with a 50px gap and section 3 is hidden. That means that the size of the content is 650px. I would expect AutoLayout to shrink the parent view to 650px. Instead, the view stays at the size drawn in Interface Builder which is 1000px and then AutoLayout stretches the sizes of the content sections to reach the 1000px. If I resize the view in Interface Builder that new size will be the rendered size. Regardless of the amount of sections showing.
I feel like I'm missing something simple but I've spent hours trying to figure this out. Any ideas?
Edit
Some clarifications. There are no height constraints on any of the sections because they can vary in height depending on the amount of data. I just used 300px each to simplify things. And I'm able to hide the sections instead of setting a zero height because in Interface Builder their are no constraints linking the sections. Those constraints are added through code at runtime because at runtime I know what needs to be linked to what. In the end. There is a solid link of vertical constraints from the top of the parent view to the bottom of the parent view. And none of the hidden sections contain vertical constraints.
Edit 2
If I defined a constraint for the height of the parent view, obviously I can modify the constant and manually set the size of view. Is there anyway to find the minimum size of a view based on dynamic content sizes and constraints?
I just read few information about how to manipulates constraints number programatically, but I think I need to see it visually using Xcode. So I decided to put a UIView like this and see what's going on with Constraints :
automatically it will create 4 constraints :
after modifying the number, I got what's the meaning of all this. there we have 2 vertical space and 2 horizontal space. but let's focus on vertical first...
one of vertical space measured from top of the screen (superview, I guess), I give this number 30. and other vertical space measured from the bottom, I give -50.
now, when it turns into code, I really don't understand how to give this 2 numbers. as far as I know, here's how to set constraint with code :
NSLayoutConstraint *myConstraint =[NSLayoutConstraint
constraintWithItem:_screenView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_container
attribute:NSLayoutAttributeHeight
multiplier:20
constant:200];
[_screenView addConstraint: myConstraint];
let's assume _screenView is a superview and _container is UIView placed on top of it. I have 3 questions :
How to set both vertical space (30 and -50) manually with code? because from storyboard I don't see any different name or ID of vertical space. they both have same name. do I have to create 2 NSLayoutConstraint ?
Then how to add it into _screenView? do I have to create this : [_screenView addConstraint: myConstraint]; twice?
what multiplier used for? because I don't see that option in storyboard.
thank you very much.
I think it's important to think of this formula from Apple's Auto Layout Guide when working with constraints:
y = m*x + b, where:
y and x are attributes of views.
m and b are floating point values.
You can think the constraint for the top of _container like this:
_containter.top = m*_screenView.top + b
When you are creating a constraint using NSLayoutConstraint constraintWithItem:... the multiplier argument is "m" and constant is "b" in this formula. To create a constraint that keeps the top of _container 30 points below the top of _screenView you would do this:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint
constraintWithItem:_container
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_screenView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:30.0];
The bottom constraint would be:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint
constraintWithItem:_container
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_screenView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-50.0];
Also keep in mind that in the UIKit coordinate system X values go up from left to right and Y values go up from top to bottom. In other words, 0,0 is at the top-left of screen and the values go up from there.
If you want to create the four contraints you show above you will have to create them all individually. You could also use the constraintsWithVisualFormat:options:metrics:views: method of NSLayoutConstraint to make multiple at once. I find it more clear to write them out the way I did above.