add subviews generated by autoayout to a parent view - ios

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.

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 do uilabel programmatically in auto layout

I am doing a project in auto layout in Xcode 6, I am adding a label programmatically its working perfectly in 4s,5,5s but in 6 and 6 plus is not working. can anyone help me, I am new to auto layout.Below is my coding.
UIScrollView *scroll = [[UIScrollView alloc]init];
[scroll setTranslatesAutoresizingMaskIntoConstraints:NO];
scroll.contentSize=CGSizeMake(480, 600);
[centerView addSubview:scroll];
NSDictionary *scrolldic = #{#"scrollview":scroll};
NSArray *scroll_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[scrollview(480)]" options:0 metrics:Nil views:scrolldic];
NSArray *scroll_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[scrollview(480)]" options:0 metrics:Nil views:scrolldic];
[scroll addConstraints:scroll_H];
[scroll addConstraints:scroll_V];
NSArray *scroll_posH = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[scrollview]" options:0 metrics:Nil views:scrolldic];
NSArray *scroll_posV = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[scrollview]" options:0 metrics:Nil views:scrolldic];
[self.view addConstraints:scroll_posH];
[self.view addConstraints:scroll_posV];
UILabel *header = [[UILabel alloc]init];
[header setTranslatesAutoresizingMaskIntoConstraints:NO];
header.backgroundColor = [UIColor blueColor];
[scroll addSubview:header];
NSDictionary *headerdic = #{#"header":header};
NSArray *header_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[header(150)]" options:0 metrics:Nil views:headerdic];
NSArray *header_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[header(30)]" options:0 metrics:Nil views:headerdic];
[header addConstraints:header_H];
[header addConstraints:header_V];
NSArray *header_posH = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-80-[header]" options:0 metrics:Nil views:headerdic];
NSArray *header_posV = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[header]" options:0 metrics:Nil views:headerdic];
[self.view addConstraints:header_posH];
[self.view addConstraints:header_posV];
see the above image in 4s label is in center is correct , but in 6 it move to some left, what is the problem can any one help me.
To get it to work, it is also important how you want the layout to look on different devices. Should the label always have the width 150 and be centered or should it always have a 80 left and right padding?
This is what you have to decide but the constraints would look like this:
First case (same width and centered) :
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:header attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem: scroll attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0];
[scroll addConstraint:centerXConstraint];
Second case (keep the padding between devices):
[scroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-leftPadding-[header]-leftPadding-|" options:0 metrics:#{ #"leftPadding": #(80) } views:#{ #"header": header }]];
Let me know hot it goes or if you need more help.
Try adding your constraint like this
NSLayoutConstraint *leadingConstraint= [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
NSLayoutConstraint *topConstraint= [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[self.contentView addConstraints:#[leadingConstraint,topConstraint]];
The issue seems to be that you have hard coded the values in. This is fine for iPhone 4 and 5 as they both have screens with width of 320. This means your hard coded value of 80 keeps it in the middle.
iPhone 6 though has a wider screen
iPhone 4 and 5
|---80---|--150--|---90---|
You can see this with a screenshot of your screenshot above:
Here I have added two green lines both the same length, although you think your label is in the middle on iPhone 4 and 5 it actually isn't.
What you need to do is one of two things:
Use NSLayoutConstraints to fix the view in the middle instead of setting values. There are various layout constraints that put it in the middle.
You can use centreX for this
NSLayoutConstraint * centreConstraint = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:**NSLayoutAttributeCenterX** multiplier:1.0 constant:0];
Then add this constraint (NSLayoutConstraint can be quite fiddly so make sure you test it as this might not work)
This sets the label in the middle of the screen (hence the centreX - meaning centre of the x-axis)
The other way, not recommended if you are trying to have both landscape and portrait orientations, would be to calculate the width of the screen and so enter an accurate half way padding value.
You can calculate the width of the screen with
[UIScreen mainScreen].bounds.size.width
Then you can calculate the value you need it to be Eg in your case it would be
// Your label is 150 pixels wide so the space on each side is the width less 150 but half as you have the same amount on each side.
([UIScreen mainScreen].bounds.size.width - 150)/2
This is a way to simply calculate the value needed and is not as good as using NSLayoutConstraint. It is much simpler but much less versatile. I only include it for completeness.
My advice, look on some tutorials for NSLayoutConstraints this one is good and get better understanding of what you are constraining.

How to automatically change height of UILabel object and position of other elements below, based on the content of UILabel, with autolayout enabled

I've found some answers related to this topic, but nothing works for me. I've tried setPreferredMaxLayoutWidth:, setting number of lines to 0, setting height constraint
to XYZ, or equal or greater than XYZ... and all that in many different combinations. What could be possibly wrong? Any ideas?
Selected label is the one that needs to change the height based on content. Label below it, and possible other elements below should move down if the label has content that doesn't fit in 1 line. There are no constraint problems reported by IB.
Here's how I've just successfully done it:
I set numberOfLines on the label to 0, so it will grow and shrink as necessary.
I gave the label >= 0 left and right leading/trailing space constraints to the container margins, so it can grow to a maximum width.
I did not put any height constraint on the label. The height will therefore be determined by the content.
I made sure that no vertical constraints on anything below the label were limiting its downward growth.
In particular, bear in mind that if you set a constraint from anything to the bottom of the screen, you'll need to make sure that its priority (or the priority of another vertical constraint in the chain from the label to the bottom) is set to a lower priority than the vertical Content Compression Resistance Priority of the label. This will make sure that the growth of the label's content can overcome the other vertical constraints.
If you are using Auto Layout in code, setting the frame does not work. You have to create the constraint set required for the layout you want. For this case you would need to add height constraints for your UILabel.
Full tutorial on how to do is here : http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/
if you want to try to take care of it in code then you can approach it like this. i've laid out something akin to what you have then after 5 seconds it will swap in a new vertical constraint to make one of the labels taller. hope it steers you in the right direction ... or at least a direction!
NSArray * vertConstraint;
UIImageView * imageView = [[UIImageView alloc] init];
UILabel * labelOne = [[UILabel alloc] init];
UILabel * labelTwo = [[UILabel alloc] init];
UILabel * labelThree = [[UILabel alloc] init];
imageView.backgroundColor = [UIColor grayColor];
labelOne.backgroundColor = [UIColor redColor];
labelTwo.backgroundColor = [UIColor blueColor];
labelThree.backgroundColor = [UIColor orangeColor];
[imageView setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelOne setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelTwo setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelThree setTranslatesAutoresizingMaskIntoConstraints: NO];
[self.view addSubview:imageView];
[self.view addSubview:labelOne];
[self.view addSubview:labelTwo];
[self.view addSubview:labelThree];
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
NSDictionary * viewsDictionary = NSDictionaryOfVariableBindings(imageView, labelOne,labelTwo,labelThree,topGuide, bottomGuide);
// initial vertical constraints. will be swapped out after 5 seconds (See below
vertConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topGuide]-100-[imageView(==200)]-20-[labelOne(==20)]-20-[labelTwo(==20)]-20-[labelThree(==20)]-(>=5)-[bottomGuide]|" options:0 metrics: 0 views:viewsDictionary];
[self.view addConstraints:vertConstraint];
// horizontal constraints for all the elements
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[imageView(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelOne(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelTwo(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelThree(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
//additional constraints to center them
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelOne
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelTwo
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelThree
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
//delay 5 seconds then swap out vertical constraints
// in this case change the (==20) to (==40) for height of element
// you can edit that string more dynamically to fit your needs
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSArray * newVertConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topGuide]-100-[imageView(==200)]-20-[labelOne(==20)]-20-[labelTwo(==40)]-20-[labelThree(==20)]-(>=5)-[bottomGuide]|" options:0 metrics: 0 views:viewsDictionary];
[self.view removeConstraints:vertConstraint];
[self.view addConstraints:newVertConstraint];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.view layoutIfNeeded];
}];
});

iOS constraints not updating as expected

I have a superview with circle view and a holderview that contains 3 labels as subview and is centred to the superview as seen in image
I have added constraints to the 3 labels with respect to holderview and also added constraints to holderview with respect to superview
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(titleLabel);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-[titleLabel]-|"
options:0
metrics:nil
views:viewsDictionary];
[holderView addConstraints:constraints];
viewsDictionary = NSDictionaryOfVariableBindings(setLabel);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-[setLabel]-|"
options: 0
metrics:nil
views:viewsDictionary];
[holderView addConstraints:constraints];
viewsDictionary = NSDictionaryOfVariableBindings(repLabel);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-[repLabel]-|"
options:0
metrics:nil
views:viewsDictionary];
[holderView addConstraints:constraints];
viewsDictionary = NSDictionaryOfVariableBindings(titleLabel, setLabel, repLabel);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[titleLabel]-0-[setLabel]-0-[repLabel]-|"
options:0
metrics:nil
views:viewsDictionary];
[holderView addConstraints:constraints];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_labelView);
NSArray *constraints =[NSLayoutConstraint constraintsWithVisualFormat:#"|-[_labelView]-|"
options:0
metrics:nil
views:viewsDictionary];
[self addConstraints:constraints];
There is a feature in app where the circle shrinks. I want the holderview and its subivews to shrink dynamically. Adding the constraints works for holderview but the subviews get misaligned.
To shrink i update the frame size of the holderview as the superview frame changes.
Can anyone point out the mistakes and guide me to proper solution ?
Using auto layout and changing frame property messes up things.
Create oultest to the constraints that you want to change or animate
__weak IBOutlet UIView *settingsView;
__weak IBOutlet NSLayoutConstraint *settingsBottomConstraint;
__weak IBOutlet NSLayoutConstraint *settingsViewHeightConstraint;
Update the constrains(Never the frame!)
settingsBottomConstraint.constant = - settingsViewHeightConstraint.constant;
[settingsView setNeedsUpdateConstraints];
[settingsView layoutIfNeeded];
isSettingsHidden = YES;
Recently I have worked with animation of views with autolayout and you can find your answer here
Auto Layout constraint change does not animate
You can also use the function updateConstraints.
[settingsView updateConstraints];

Resize subviews in Autolayout visual form

I've failed on migrating my app from Autoresize to Autolayout mechanism. All I need is a view with dynamic size and subviews that will be connected to hte left top corner with the same size as their superview. In IB I've set size for PieView (My superview) as 186x186. And let the IB to generate all needed Constraints (I will create new ones in code):
PieView.m -updateConstraints()
- (void)updateConstraints {
[super updateConstraints];
[self removeConstraintsAffectingViewAndSubviews];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
CGFloat sizeCoef = 0.7f;
CGFloat percent = [self.category getFillinPercent] / 100.0f;
percent = percent > 1.0f ? 1.0f : percent;
sizeCoef += (1.0f - sizeCoef) * percent;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.f];//kDiameter * sizeCoef];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.0f];//kDiameter * sizeCoef];
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
NSDictionary *metrics = #{#"height":#100.0};
[self addConstraints:#[width, height]];
NSString *visualForm = #"H:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
visualForm = #"V:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
[self addConstraints:verticalConstraints];
[self addConstraints:horizontalConstraints];
}
The result really surprised me. Blue one displayed correctly as I want, but green one has strange behavior.
Off course I have error: Unable to simultaneously satisfy constraints.
First imageView:
NSIBPrototypingLayoutConstraint:0xa2795d0 'IB auto generated at build time for view with fixed frame' V:[UIImageView:0x8a37150(186)]>
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)]
Second imageView: the same one
Probable for PieView:
(
NSLayoutConstraint:0x8a40750 V:[CEPieView:0x8a36e50(100)],
NSLayoutConstraint:0xa030570 V:|-(0)-[UIImageView:0x8a37150] (Names: '|':CEPieView:0x8a36e50 ),
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)],
NSLayoutConstraint:0xa0305f0 V:[UIImageView:0x8a37150]-(0)-[UIImageView:0xa277cf0]>
NSLayoutConstraint:0xa030620 V:[UIImageView:0xa277cf0(100)],
NSLayoutConstraint:0xa030650 V:[UIImageView:0xa277cf0]-(0)-| (Names: '|':CEPieView:0x8a36e50 )
)
Please, give me an advise how to fix it. How to create and old autoresizing mask(UIViewAutoresizingFlexibleHeight, UIViewAutoresizingFlexibleWidth) with the help of AutoLayout mechanism. Any help will be appreciated.
The way you're setting the constraints is wrong. Basically you're asking the superview to be 100pts by 100pts, and to contains two subviews that are each 100pts by 100pts and are beside AND on top of each other. So first your superview would have to be 200pts wide and/or tall, and then you can't have two view that are both beside each other and on top.
What you really want to do (I think) is having:
NSString *visualForm = #"H:|[_imgFullCircle]|"; // No need to explicitly set the height
Then add the corresponding constraint, then:
visualForm = #"H:|[_imgEmptyCircle(height)]|";
And add another constraint for this, and do the same for vertical constraints.
Basically you'd end up with:
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];

Resources