iOS - changing constraint relation programmatically - ios

given the following constraint in ios programmatically:
IBOutlet NSLayoutConstraint *myConstraint;
this constraint is linked in interfacebuilder to the following details:
How do I change the relation attribute programmatically. I tried to look up for a method called setRelation but I don't see it.

According to the documentation, relation is read-only.
What you will need to do, I suspect, is to set
self.myConstraint.active = NO;
Then make a new NSLayoutConstraint programmatically using:
+ constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
And in the process copying values you want to keep, and replacing the relation.
Then add it to the view hierarchy where appropriate.

You can do like that :
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:self.yellowView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.redView
attribute:NSLayoutAttributeWidth
multiplier:0.75
constant:0.0]];

Related

Constraints set in code doesn't reflect in the output(Autolayout iOS)

I have 2 views that I have taken in storyboard but I haven't set any layouts in the storyboard.I want to set them by code.So here is what I did:
I read about autoResizingMask property of a view and 'translatesAutoResizingMaskIntoConstraints'.So setting 'translatesAutoResizingMaskIntoConstraints' to 'NO' makes sense.So I have done that but still I' not able to set the constraints programmatically.Now,as some constraints are automatically set in storyboard when we take the views from storyboard,I tried:
[self.view removeConstraints:[self.view constraints]];
After doing this,it removes all default constraints from the superview(i.e. self.view).But still I am not able to set constraints from code.What should I do?I need some guidance.
Here is my code that I have been using:
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *layouts1 = [NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeWidth multiplier:3.0f constant:100.0f];
[self.view addConstraint:layouts1];

Adding constraint to the "main view" in a xib

I have a UIView defined in a .xib file. I need to set translatesAutoresizingMaskIntoConstraints = NO. This means that the frame is not translated to constraints so I need to set the size constraints by myself.
I have created a working category method for a UIView:
-(NSArray*)setSizeConstraints:(CGSize)size
{
NSLayoutConstraint* height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.height];
NSLayoutConstraint* width = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.width];
[self addConstraint:height];
[self addConstraint:width];
return [NSArray arrayWithObjects:height, width, nil];
}
But I would like to set these constraints from the Xcode interface builder, but all my AutoLayout controls are greyed out:
Is there a way to do this in the interface builder?
This actually is possible.
Simply put the view you'd like to constrain into another view, like the highlighted view here.
Then, add your desired constraints.
Finally, pull the view you just added constraints to out of its parent view. You can now add constraints to that view.
As you've surely found out by now, it looks like there's currently no way to set Autolayout constraints at the main UIView level from Interface Builder.
I had a similar problem here (Launch Screen XIB: Missing Width / Height Constraints (Xcode 6)), while working with the launch screen of my app.
The only workarounds I found, if you want to keep working with IB, are:
Using a UIViewController and its UIView inside the XIB, instead of just a UIView
Working only with Storyboards (and, once again, UIViewController)
However, I understand these approaches may not be ideal or "clean", especially if you simply have a subclass of UIView and you want to draw its interface in a good old XIB.

IBOutlet didn't work with Hard Code constraints

I have new issue related to Auto-Layout world , i can summarize the problem in the below steps:
1- I have storyboard with only one scene contain UIImageView .
2- I went to viewcontroller.m file and add manual constraints like below code
[self.bgImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *horizentalSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.bgImageView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraint:horizentalSpaceConstraint];
Result :
The constraint didn't affect the IBoutlet for UIImageView but if i add UIImageView from hard coded in viewcontroller.m file it works ,can you help me to discover this problem.
I found the solution which can describe by ( Every constraints used IBOutlet elements should before begin add only the below line without need to [setTranslatesAutoresizingMaskIntoConstraints :No]
-(void)viewWillAppear:(BOOL)animated{
// Step 1 remove view constraints for IBOutlet elements
[self.view removeConstraints:self.view.constraints];
}

iOS - constraints animate in an unexpected way in viewdidload?

I'm practicing auto layout and learning about animating constraints.
My first question is. If I am dynamically adding views it seems cumbersome to dynamically add their constraints to the parent view as well. Is there any clean way to accomplish a flexible layout where views can be added and removed programmatically? Or would this mean I should probably think of a simpler solution for what I'm trying to accomplish?
Second question. I have created two views, and some constraints in code. I am just trying to resize the height constraint on the first view on load so that it will become shorter, and the second view will shift upwards accordingly.
here is some code:
first = [[UIView alloc]initWithFrame:CGRectZero];
[first setBackgroundColor:[UIColor blueColor]];
[first setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:first];
UIView *second = [[UIView alloc]initWithFrame:CGRectZero];
[second setBackgroundColor:[UIColor redColor]];
[second setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:second];
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:20];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:-20];
top = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:40];
height = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:80];
[self.view addConstraints:#[leading,trailing,top,height]];
[height setConstant:10];
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
okay so at the bottom here I run my animation.. my second view is already positioned where it would be at the end of the animation. the first view expands from its top left corner, to its bottom right corner. it animates diagonally and ends up with a height of 10.
Can anyone explain this behavior. I noticed if I assign the constraints, and make them animate on an IBAction (button touch) then it will animate as expected.
Second question first. Why are you animating changing in viewDidLoad? At this point we know the view is loaded, but it probably isn't laid out and definitely not going to be visible to the user; consider the constraint layout changes in the viewDidLayoutSubviews method.
As for the first question. David H's answer is one way... and a perfectly fine way. To give a different option, I use constraintsWithVisualFormat:options:metrics:views: which allows me to specify views, then creates all the necessary constraints across all the views. It can be a much simpler way to create constraints across several views. Depending on exactly what you are doing one way might better suit your needs.
Edit based on comment...
With something where you'll have to break constraints, you'll still have to find and break the constraints before creating the new ones. There is no way around that. You'll either have a reference to the constraint you want to break or have to iterate through all the constraints on an object to find it. A B C goes to A B and C where the constraint between B and C is gone. Using the visual format to put in X might be something like #"[B]-20-[X]-20-[C]" which will create a constraint for a 20 point spacing between B and X and a second constraint which will be a 20 point spacing between X and C. As a note, the visual format above specifies horizontal positioning/spacing only. You would need a second line to specify the vertical constraints.
I am doing something similar to this, and the technique can be extended. For each view (really, any object), create a mutable dictionary with a "view" and a "constraints" property. the view is just the view, the constraints are an array of dictionaries containing two objects, a "view" property, and a "constraint" property.
When you decide to add or pull a view, then find the dictionary with the appropriate view property, then interate over the constraints array, and add/remove the constraint (of type NSLayerConstraint) to the sister "view" property in the dictionary.
In this manner you can in one method add and remove all the proper constraints regardless of what they view they affect.
Obviously you need to only have constraints that reference views still in the primary view. However, another way to deal with that is to set the width/height of a view to 0, its still there but is not visible. Or change its alpha to 0.

AutoLayout: Centering two views in code causes constraint exception?

Short version: Creating a NSLayoutAttributeCenterX constraint always causes a constraint failure. Why?
I have a UITextField subclass which behaves as a search textbox. You tap the box and a list of items appear, the items are filtered by what you type. Like this:
The UITextField subclass is responsible for creating and displaying the view of items. The center of the list should match the textfield's center. So, I set my constraint like this:
NSLayoutConstraint *hugCenter =
[NSLayoutConstraint constraintWithItem:self.searchContainerView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
This invariably leads to the error:
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x9a5e1a0 UIView:0x9a5ba40.centerX ==
SearchTextField:0x7154a40.centerX
For testing I've made my scene as simple as possible -- 1 textfield on a view controller:
Why does this cause a constraint error?
Are you missing a setTranslatesAutoresizingMaskIntoConstraints: somewhere?

Resources