I have pretty simple UI that works well with constraintsWithVisualFormat, I tried to replace that with constraintWithItem and for some reason it is not working. I don't know what's wrong here.
The literal sentence I understood is
Vertically, the contentView should fill the entire height of its
superview with no padding.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
//Below is my alternative code NOT working, but this should work too?
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
Your new code specifies the height, but not position. The visual format specification says nothing about the item’s height, but instead pins the top and bottom edges of the views to each other.
Instead of creating constraint for height, create two constraints, one for top margin, and one for bottom margin, pinning the edges of contentView to containerView.
You should change it from height to top. And also add similar constraint to bottom.
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
This visual format:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
should be replaced with two constraints:
NSLayoutConstraint *topConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint: topConstraint];
and
NSLayoutConstraint *bottomConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[containerView addConstraint:bottomConstraint];
Related
I have Two View say view1 and view2.
- view2 should be at 2 pixel distance from view1 (vertically) and
- Height of view2 should be such that bottom of view2 must be same as bottom of viewcontroller.
How to specify the constraint for this in code ?
Since I don't know what you already have in your view hierarchy I've set up a simple example. Look into this bit of code. It creates two new UIViews and position them using constraints.
- (void)viewDidLoad {
[super viewDidLoad];
UIView *view1 = [UIView new];
UIView *view2 = [UIView new];
view1.backgroundColor = [UIColor redColor];
view2.backgroundColor = [UIColor blueColor];
[self.view addSubview:view1];
[self.view addSubview:view2];
[view1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[view2 setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(100)-[view1(200)]-(2)-[view2]|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[view1]|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[view2]|" options:0 metrics:0 views:views]];
}
Here's the result:
Code is not that hard to understand. + constraintsWithVisualFormat: method creates an array of NSLayoutConstraints that you add to the superview of your custom views. Look here for more on visual format language. It's very effective at simple layout and saves a bunch of time compared to creating constraints one by one.
If you reeeaaally want to create constraints using constraintWithItem: method, here's your code:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:100]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view1 attribute:NSLayoutAttributeBottom multiplier:1 constant:2]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view2 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:200]];
It results in the same constraints, but takes up twice the space and is harder to read.So I strongly recommend you to use visual format instead. You should only use constraintWithItem: for more complicated layout which visual format can't always handle.
I have a XIB view.
When i added it to my view, i want the width and height to fit the iOS device.
However, it doesn't fit.
If i use autolayout it would fit the screen. But how do i use it ?
In the code example below, it takes the Bounds of the XIB file. Therefore it doesn't fit the view. How can i solve this?
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.myView=[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] lastObject];
self.bounds=self.myView.bounds;
[self addSubview:self.myView];
}
return self;
}
Load your view, then
[_subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[_contentView addSubview:_subview];
[self activateConstraintsForView:_subview respectToParentView:_contentView];
Use this function :
- (void)activateConstraintsForView:(UIView *)view respectToParentView:(UIView *)parentView
{
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0];
[NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:topConstraint, leftConstraint, bottomConstraint, rightConstraint, nil]] ;
}
Hope, it helps.
In your initWithFrame method add this at the end:
[self setConstraintsOfView:self.myView withRespectToParentView:self];
And add the method below at the end of your class:
-(void)setConstraintsOfView:(UIView *)subView withRespectToParentView:(UIView *)parentView
{
NSDictionary *viewsDictionary = #{#"subView":subView};
NSDictionary *metrics =#{#"offset":#0};
NSArray *heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-offset-[subView]-offset-|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *widthConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-offset-[subView]-offset-|"
options:0
metrics:metrics
views:viewsDictionary];
[parentView addConstraints:heightConstraint];
[parentView addConstraints:widthConstraint];
}
VFL is much easier than setting each constraints. This method sets the top, bottom, leading and trailing space = 0 of subview with respect to superview.
I think you want to achieve this though:
https://github.com/PrajeetShrestha/ConstraintTest
I have searched for this problem all over the programming site but i did not get the answer. So I am writing here.
I have a tableViewController, in that i am using custom cell using xib. It contains 4 labels, Each one is appeared by bottom of others. Now I feed the content programmatically, so label1 has number of lines and label2 has number of lines. Now the problem is the contents of label1 and label2 is messed up. I am using the following code:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.label1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.label2 attribute:NSLayoutAttributeTop multiplier:1 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.label1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.image1 attribute:NSLayoutAttributeTop multiplier:1 constant:0.0]];
NSArray *constraint_H_label1 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[goalDescription(274)]" options:0 metrics:nil views:contentDictionary];
NSArray *constraint_POS_H_label1 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-18-[label1]" options:0 metrics:nil views:contentDictionary];
NSArray *constraint_POS_V_label1 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-31-[label1]" options:0 metrics:nil views:contentDictionary];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.label1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.label2 attribute:NSLayoutAttributeTop multiplier:1 constant:0.0]];
What I want is, if label1 has 4 lines then the label2 should start after the 4th line of label1. But I dont know how to use this without activating autolayout in xib.
Can anyone help me to solve this issue?
Thanks in advance.
I have four labels stacked one below the previous one but alighning its baseline with the top of its content view, not with a vertical spacing with each other.
I do it by code this way
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:topFirstLabel_
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:20.0f]];
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:topSecondLabel_
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:47.0f]];
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:topThirdLabel_
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:70.0f]];
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:topFourthLabel_
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:87.0f]];
Now, I want all labels to be aligned by the trailing space with its superview.
Can I do that with an unique VFL string? Something like this, although this example will crash the app:
NSDictionary *views = NSDictionaryOfVariableBindings(contentView, topFirstLabel_, topSecondLabel_, topThirdLabel_, topFourthLabel_);
NSDictionary *metrics = #{ #"bigMargin" : #12 };
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[topFirstLabel_][topSecondLabel_][topThirdLabel_][topFourthLabel_]-bigMargin-|"
options:NSLayoutFormatAlignAllTrailing
metrics:metrics
views:views]];
This library might help you. It uses a vertical linear layout concept and you can add paddings as needed.
I don't think you can do this in a single call.
You can probably do something like this:
for ( NSString* viewName in views.allKeys )
{
[contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat: [NSString stringWithFormat: #"H:[%#]-bigMargin-|", viewName], options:NSLayoutFormatAlignAllTrailing metrics:metrics views:views];
}
If I have a view in my app with 3 subviews (as an example) with the following spacing between them:
TOP - 40 points - SUBVIEW 1 - 60 points - SUBVIEW 2 - 80 points - SUBVIEW 3 - 80 points - BOTTOM
I understand how I can use auto layout to ensure each of my subviews keeps it's height and width, and I can get everything to be aligned on either a 3.5 OR 4 inch iPhone screen.
But I can't work out what kind of constraint I need to make it so that if it were aligned for a 3.5" screen, and then went to a 4" screen, each spacing would increase proportionally (e.g. 40 points would go to 47, 60 to 71, 80 to 95 - or thereabouts).
Is this possible? Or do I need to make all the spacing equal between the elements? (If so how would I still get it to resize equally?)
I'm new to Auto Layout so if I've missed anything off, or haven't made it clear what I means please let me know, thank you.
The Trick is to add not only one but TWO constraints between your views. One with "Greater Than or Equal" and one with "Less Then or Equal".
The minimum size (Greater Then or Equal) should be the spacing on the 3.5" display.
The maximum size (Less Then or Equal" should be the spacing on the 4" display.
Example:
TOP - 40 points - SUBVIEW 1 - 60 points - SUBVIEW 2 - 80 points - SUBVIEW 3 - 80 points - BOTTOM
TOP - SUBVIEW1:
Select both in Interface Builder. Add the constraint "Vertical Spacing" two times.
Set one to Greater Then or Equal 40.
Set the other to Lesser Then or Equal 47.
The sum of all Greater Then values + all heights of your views should be 480 pixel (3.5")
The sum of all Lesser Then values + all heights of your views should be 568 pixel (4")
I don't know any easy way to do this. I made one where all the spaces were equal, but to do that, I had to fill the spaces with invisible labels (just label with no title). So, I had my 4 visible objects and the 5 "spacer labels" all right on top of each other. I made the 4 visible objects all have explicit heights, and the spacers with no fixed heights, but set to be all the same:
-(void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *viewsDict = [NSMutableDictionary dictionary];
for (int i=1; i<5; i++) { // Labels with titles
UILabel *b = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 44)];
b.text = #"This is my label";
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
[viewsDict setObject:b forKey:[NSString stringWithFormat:#"b%d",i]];
}
for (int i=1; i<6; i++) { // Spacer labels
UILabel *l = [[UILabel alloc ]init];
[l setTranslatesAutoresizingMaskIntoConstraints:NO];
[viewsDict setObject:l forKey:[NSString stringWithFormat:#"l%d",i]];
}
for (id obj in viewsDict.allKeys)
[self.view addSubview:viewsDict[obj]];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[l1][b1][l2(==l1)][b2][l3(==l1)][b3][l4(==l1)][b4][l5(==l1)]|"
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:viewsDict];
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:#"|-[b1]"
options:0
metrics:nil
views:viewsDict];
[self.view addConstraints:constraints];
[self.view addConstraints:constraints2];
}
To make the spaces different, I think you have to use the longer form of expressing constraints, rather than the visual format. The below code seems to work for me. I'm using the same definition of the views as above, except that I cut the number of titled labels to 3 and spacers to 4 to match you question. The relative spacing should be as in your example, 2:3:4:4.
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:viewsDict[#"l1"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b1"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b1"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l2"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l2"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b2"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con5 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b2"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l3"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con6 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l3"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b3"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con7 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b3"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l4"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con8 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l4"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *con9 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l2"] attribute:NSLayoutAttributeHeight multiplier:.66 constant:0];
NSLayoutConstraint *con10 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l3"] attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
NSLayoutConstraint *con11 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l4"] attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
NSLayoutConstraint *con12 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b1"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSLayoutConstraint *con13 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b2"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSLayoutConstraint *con14 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b3"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSArray *constraints = #[con1,con2,con3,con4,con5,con6,con7,con8,con9,con10,con11,con12,con13,con14];
[self.view addConstraints:constraints];