constraintsWithVisualFormat set same Y position for all the views - ios

I am just playing with constraintsWithVisualFormat but not able to figure out how to do.
I have two Views placed together with 10 pix margin in horizontal. and Y position will be the same for the both of the views.
I have tried the following things.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1(==100)]-[view2(==100)]" options:0 metrics:nil views:views]];
Horizontal Constraint : 10 pix left margin from superview - 100 width
of view1- 10 pix margin - 100 width of view2
Vertical Constraint : 10 pix top from superview - 100 height of view1-
100 height of view2
Problem
The second view Y position start after the view1 i want the same Y position for the both of the view.
Please if possible explain the answer

Mark '-' is margin 8px in VisualFormatLanguage.
Try this.
`[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1(==100)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view2(==100)]" options:0 metrics:nil views:views]];`

Horizontal layout with all bottom alignment:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view1(==100)]-10-[view2(==100)]"
options:NSLayoutFormatAlignAllBottom
metrics:nil
views:views]
Vertical layout untouched:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[view1]"
options:0
metrics:nil
views:views]
Please also note that two approaches -- constrain each view at top of superview vs. align all views around common top/bottom/baseline/centery differ in obvious way. And that other answer cannot address the case of center-alignment, btw.
UPD: Here is a complete working example of using alignment options in layout:
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSTextField *view1 = [[NSTextField alloc] initWithFrame:NSZeroRect];
NSTextField *view2 = [[NSTextField alloc] initWithFrame:NSZeroRect];
NSView *contentView = [self.window contentView];
NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);
[view1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[view2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[contentView addSubview:view1];
[contentView addSubview:view2];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view1(==100)]-[view2(==100)]"
options:NSLayoutFormatAlignAllBottom
metrics:nil
views:views]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[view1]"
options:0
metrics:nil
views:views]];
}

Related

AutoLayout programmatically doesn't work

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(#"-----");
}

How to add two views on each other with constraints

I am trying to add 2 views with same X, Y. They have space from the edge using addConstraints:[NSLayoutConstraint constraintsWithVisualFormat...]
I tried the strings "H:|20-[A][B]-20-[C]-20-[D]-20|" and V:|20-[A][B]-20-[C]-20-[D]-20|, and centreX == CenterX and centerY==centerY, but they kept conflicting, thinking that A and B should be next to each other.
A is hidden, and on some button clicked, B is hidden and A is shown.
To add views with same centre X,Y You need to first place one of the view say bView (Bottom view), using constatins
NSArray * bVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)];
NSArray * bHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)];
This places the bView in the superview with 20 as edge offset.
Now place the tView (top View) with same centre as of bView. Here self is the superview of bView and tView
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
Then pin the edges of tView with desired offset say 40
NSArray * tVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-40-[tView]-40-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)];
NSArray * tHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-40-[tView]-40-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)];
EDIT :
Here is how do do it.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
bView = [UIView new];
bView.backgroundColor = [UIColor redColor];
bView.translatesAutoresizingMaskIntoConstraints = NO;
tView = [UIView new];
tView.backgroundColor = [UIColor blackColor];
tView.translatesAutoresizingMaskIntoConstraints = NO;
// Firdt bView is added then tView hence tView is exaclty above the bView.
[self.view addSubview:bView];
[self.view addSubview:tView];
// Edges Constrints for bView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)]];
// Edges Constints for tView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[tView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[tView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)]];
// Centring for bView and tView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
}
-(IBAction)toggleViews:(id)sender {
NSArray * subViews = self.view.subviews;
[self.view exchangeSubviewAtIndex:[subViews indexOfObject:tView] withSubviewAtIndex:[subViews indexOfObject:bView]];
}

Change constraints based on text length

I have two labels, that are ordered like this:
H:[label1]-10-[label2]
I want to change their order, based on the first label's length. So, if its text won't fit on one line, I want to change the constraints to look like this:
V:[label1]-2.5-[label2]
Here are my constraints right now:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]-2.5-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_statusLabel]|" options:0 metrics:nil views:views]];
And this is how I want them to look, when _carLabel won't fit in one line:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]-10-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_statusLabel]|" options:0 metrics:nil views:views]];
How would I do this?
I think your best bet would be to conditionally apply constraints based on the size of the label. I don't believe there's a way to add both constraints and have this work how you described. (Though if it is possible it will likely have to do with setting the priority of each constraint #"[label1]-10#99-[label2]" and #"V:[label1]-2.5#98-[label2] but I was unable to get that to work. The code below however seems to do what you're looking for.
UILabel *label1 = [[UILabel alloc] init];
label1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label1];
[label1 setText:#"Small text"];
// [label1 setText:#"Some really long text to test the other scenario also works"];
UILabel *label2 = [[UILabel alloc] init];
label2.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label2];
[label2 setText:#"Small text 2"];
[label1 sizeToFit];
if (label1.frame.size.width + kPadding + label2.frame.size.width < self.view.frame.size.width) {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[label1]-padding#100-[label2]"
options:0
metrics:#{#"padding": #(kPadding)}
views:NSDictionaryOfVariableBindings(label1, label2)]];
} else {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label1]-2.5#100-[label2]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label1, label2)]];
}

uiscrollview doesn't scroll because of autolayout

I know that there are already many answers to my questions, but I've tested each one of these and I still have the problem.
I have a UIScrollView which contains a UIView, I want active autolayout for an animation. But because of this, my scrollview doesn't scroll.
This is my code:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollView, _containerScrollView);
[_scrollView setScrollEnabled:YES];
[_scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, CGRectGetHeight(_containerScrollView.frame))];
_scrollView.userInteractionEnabled = YES;
_scrollView.delaysContentTouches = YES;
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
Somebody can explain me why doesn't it work?
Try to set your scrollView's Content size int "viewDidLayoutSubviews" method with keeping the autolayouts set.
-(void)viewDidLayoutSubviews
{
[self.itemList setContentSize:CGSizeMake(required_width, required_height)];
}

NSLayoutConstraint setup

I'm trying to setup a panel to display some information. I'm having trouble getting the autolay out to work as i want.
here's what I'm trying to do:
I have a headerView, a UIImageView and 5 labels. I want the labels to line up vertically and i want them 8 spacing from the UIImageView. I can line them up vertically easily enough:
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView]-[acresLabel]-[addedLabel(==acresLabel)]-[typeLabel(==acresLabel)]-[zonesLabel(==acresLabel)]-[sceneLabel(==acresLabel)]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(headerView, acresLabel, addedLabel, typeLabel, zonesLabel, sceneLabel)]];
But getting them to line up off the image view is proving tedious/verbose:
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[acresLabel]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(imageView, acresLabel)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[addedLabel]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(imageView, addedLabel)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[typeLabel]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(imageView, typeLabel)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[zonesLabel]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(imageView, zonesLabel)]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[sceneLabel]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(imageView, sceneLabel)]];
Seems like a better way to do this then set up a constraint for each label to the image view. I tried adding NSLayoutFormatAlignAllCenterX to the first constrain, but it sets them all center to the content view's center X, doesn't line the label's center X up with each other. NSLayoutFormatAlignAllBaseline gives an error and crash when used vertically.
I'll have to do this again for the data (Acres: 'value').
You're on the right track with NSLayoutFormatAlignAllCenterX. All you need to do differently is isolate V:[headerView]-[acresLabel] from the rest of the constraint, since you don't want headerView's CenterX to be aligned with the 5 labels'.
// NSDictionary* views ;
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView]-[acresLabel]" options:0 metrics:nil views:views] ;
// Use any of the horizontal NSLayoutFormats (NSLayoutFormatAlignAllLeft, NSLayoutFormatAlignAllRight, NSLayoutFormatAlignAllLeading, NSLayoutFormatAlignAllTrailing, NSLayoutFormatAlignAllCenterX)
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[acresLabel]-[addedLabel(==acresLabel)]-[typeLabel(==acresLabel)]-[zonesLabel(==acresLabel)]-[sceneLabel(==acresLabel)]-|" options:NSLayoutFormatAlignAllCenterX metrics:0 views:views] ;
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-[acresLabel]" options:0 metrics:nil views:views] ;

Resources