I am having an UIImageView (say imageView1) and a UITextView(say textView1) which have to be displayed vertically (one [imageView1] below the other [textView1]) beginning with the same margin position as of textView1. I have to achieve this through autolayout programmatically.
I know that this can be done by setting the vertical constraints like below for both the views.
NSLayoutConstraint constraintsWithVisualFormat:#"V:|[textView1]"
But the problem I have here is I already have many text views(textView2, textView3) arranged in horizontal before and after this textView1.
I have already added many autolayout constraints to this textView1 through storyboard. Based on the different screen size and orientation the textView1 margin differs as per the constraints that are provided on the storyboard for this.
Now how can I provide the autolayout constraint programmatically in such a way that my imageView1 is to align in par vertically with the same margin as that of textView1?
p.s: imageView1 is created programmatically in code but where as all other views that I mentioned above are created through storyboard.
+ Adding images for easy understanding
In the image, imageView1 is the UI Image. I have created it in storyboard just for understanding purpose but in real it will be created programmatically and this have to be aligned to the margin of UITextView (textView1) present below it.
This is the constraint that I want to create it through programmatically(In case this is the real question here :).
This constraint is to always make sure that imageView1 and textView1 start originating from the same margin.
How to define this constraint programmatically ?
Rather than using the visual format, you can just instantiate a constraint directly, e.g.
[NSLayoutConstraint
constraintWithItem:imageView1 attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:textView1 attribute:NSLayoutAttributeLeading
multiplier:1.0 constant:0.0]
Use Masonry for setting constraints programmatically.It is very easy to use and reduce lots of complexities for the user.
https://github.com/SnapKit/Masonry
...you can try to build an UIView, set the constraints that it will need, and use it as a placeholder for your UIImageViews.(later you can add them inside of such a view) or, by the otherside, using an UICollectionView instead.
You can set the options argument in
constraintsWithVisualFormat:options:metrics:views: check Apple Class Reference.
Your code might be as follows
NSString* leadingConstraintsExpression = #"V:[imageView1][textView1]";
NSDictionary* viewsDictionary = NSDictionaryOfVariableBindings(imageView1,textView1);
NSArray* leadingConstraints = [NSLayoutConstraint
constraintsWithVisualFormat:leadingConstraintsExpression
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:viewsDictionary];
[self.view addConstraints:leadingConstraints];
I am developing an app in iOS.I have used autolayout ,constraints to build the UI.But still i am very much confused about it.I am not able to create accurate UI using autolaout & size classes.Please pardon me if i am asking very basic question because i am very fresh to iOS.
Here I have created a Login screen.In which i have a container in middile which contains some text filed & image at top which is kind of app logo and below the container i have two buttons.I have three problems.
Container is not aligned properly.It size keep on increasing & margin between container & text fields is increasing.So UI looks bad.I want to increase width & height in equal proportion so that it does not look bad.
2.The size of logo should be increase in equal proportion.It's height & width.
3.Buttons at bottom their height is fixed.Their height should increase in equal proportion.
ScreenShot
Hi if you want keep proportion you need to work with aspect ratio constraints as such:
NSLayoutConstraint *constraint =[NSLayoutConstraint
constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:childView
attribute:NSLayoutAttributeWidth
multiplier:4.0/3.0 //Aspect ratio: 4*height = 3*width
constant:0.0f];
[view addConstraint:constraint];
One more thing you need to know is not to limit the view grow with other constraint.
Lets say you want the top image to grow don't add some fix leading or trailing from the side, just add top constraint and center the view to its superview.
If you want to limit the image size so it want get out of superview bounds just add a constraint leading and trailing with gratherOrEquelTo relation
Hope I was clear.
//Edit
If you want some minimum size for height or width just add a constraint of height or with the minimum size you want with the relation of gratherOrEquelTo
Is it possible to have a unidirectional size match using autolayout and interface builder?
For example, I might have two labels. I don't want label A to be larger than label B, and I want B to have its intrinsic size. But using "match heights/widths" could result in a large amount of text increasing A's size, and therefore B's.
The way to do this would be to have two constraints.
An equal heights constraint between the label and the image view.
A height constraint on the image view.
This will first set the height of the image view with the fixed height constraint and then will set the height of the label from the height of the image view (equal heights).
By doing this the label will not grow with the amount of text it has. Its height is effectively fixed by the image view.
It will not make the image view get any bigger as this would contradict the fixed height.
EDIT FOR NEW QUESTION
OK, for this you would do pretty much the same thing. It might be a bit tricky in interface builder though as I'm never sure which is item1 and item2 in the constraint when done through IB.
You could do it very easily though by adding one line of code...
[theSuperview addConstraint:[NSLayoutConstraint constraintWithItem:labelA
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:labelB
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0.0]];
This is exactly what the interface builder constraint does but I'm not sure if you can tell which way around the item1 and item2 are.
This is your "unidirectional" equal heights attribute though.
EDIT 2
There may or may not be an update some time in the future that might let you see item1 and item2 in interface builder.
I'd like to map dots along a line.
The line length depends on device and orientation and is always stretched across the whole screen. Therfore it would make most sense to me to position the dots using a relative percentage value.
So far I only found constraints being defined in some sort of point value (Doc)
Is it possible to use percentage values as constraints as well? Any ideas on how to position these dots in a scalable way ... or do I need do this conversion/positioning "manually"?
Yes, you can position the dot as a fraction of the view's width. The NSLayoutConstraint method, constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:, has that multiplier parameter that lets you use a fractional relationship between a superview and its subview. The superview's right edge will be the width of that view (the screen if its a full width view), so if you create a constraint like below, the dot will be positioned at a fractional distance along the line:
-(void)viewDidLoad {
[super viewDidLoad];
[self.view removeConstraint:self.leftConDark];
[self.view removeConstraint:self.leftConLight];
NSLayoutConstraint *lcd = [NSLayoutConstraint constraintWithItem:self.darkButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:.5
constant:0];
NSLayoutConstraint *lcl = [NSLayoutConstraint constraintWithItem:self.lightButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:.9
constant:0];
[self.view addConstraints:#[lcd,lcl]];
}
In this example I'm positioning two UIButtons (info type dark and light). I added them in IB, and made IBOutlets to their constraints they have to the left side of the view (that's what the system gave me, it could have been to the right side -- it doesn't matter since you just delete them anyway. If you are making the dots in code, you wouldn't need to do this). In code I remove those constraints, then add new ones that will put the center of the buttons at 50% and 90% of the way across the screen.
I'm not an auto-layout expert (or even competent yet!) but as of XCode 6, you seem to be able to do this in IB.
Select the object.
Show the size inspector (click on ruler)
Double-click on the constraint rectangle (not obvious! "Edit" takes you somewhere else)
Here you can set the multiplier just like in the answer above.
I'm experimenting with how to use UIScrollView. After much trouble, I finally got the hang of it. But now I've seem to hit another snag.
In this simple app, I have a scroll view with and in order for it to work, I have to set the view's bottom space to scrollview constraint to 0 as described here and it works fine. I'm doing it through the IB.
Now I've come across a scenario where I have to do that part programmatically. I've added the below code in the viewDidLoad method.
NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraint:bottomSpaceConstraint];
But it doesn't seem to work. It outputs the following message in the console window adn i don't know what to make of it.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"",
""
)
Can someone please tell me how to do this? I've also attached a demo project here so that you can get a better idea on the issue.
UPDATE:
First off thanks for the responses. Using the ways mentioned in the answers I was able to get it working. However ina slightly different scenario its not. Now I'm trying to load a view onto a viewcontroller programatically.
If I may explain further. There are 2 view controllers. First one being a UITableViewController and the second one a UIViewController. Inside that a UIScrollView. Also There are multiple UIViews and some of those views' height exceeds the normal height of the screen.
The UITableViewController displays a list of options. Based on the user's selection, a particular UIView out of the lot will be loaded into the UIViewController with the UIScrollView.
In this scenario, the above method doesn't work. The scrolling isn't happening. Do I have to do something different since I'm loading the view separately?
I've uploaded a demo project here so that you can see it in action. Please see the Email.xib and select Email from the table view list.
Based upon a review of your code, a few comments:
You generally don't need to adjust constraints when a view appears. If you find yourself doing this, it often means that you haven't configured your storyboard correctly (or at least, not efficiently). The only time you really need to set/create constraints is either (a) you're adding views programmatically (which I'm suggesting is more work than it's worth); or (b) you need to do some runtime adjustment of constraints (see third bullet under point 3, below).
I don't mean to belabor it, but your project had a bunch of redundant code. E.g.
You were setting the frame for the scroll view, but that is governed by constraints, so that does nothing (i.e. when the constraints are applied, any manually set frame settings will be replaced). In general, in auto layout, don't try changing frame directly: Edit the constraints. But, no changing of constraints is needed at all anyway, so the point is moot.
You were setting the content size for the scroll view, but in auto layout, that, too, is governed by constraints (of the subviews), so that was unnecessary.
You were setting constraints for the scrollview (which were already zero), but then you weren't adding the view from the NIB into the scrollview, defeating any intent there, too. The original question was how to change the bottom constraint of the scroll view. But the bottom constraint for that is already zero, so I see no reason to set it to zero again.
I'd suggest a more radical simplification of your project:
You're making life much harder on yourself by storing your views in NIBs. It's much easier if you stay within the the storyboard world. We can help you do the NIB stuff if you really need to, but why make life so hard on yourself?
Use cell prototypes to facilitate the design of the cells in your table. You can also define the segues to go from the cells to the next scene. This eliminates any need to write any didSelectRowAtIndexPath or prepareForSegue code. Clearly, if you have something you need to pass to the next scene, by all means use prepareForSegue, but nothing you've presented thus far requires that, so I've commented it out in my examples.
Assuming you were looking for a practical example of programmatically changing constraints, I've set up the scene so that the text view will change its height programmatically, based upon the text in the text view. As always, rather than iterating through the constraints to find the one in question, when altering an existing constraint that IB created for me, I think it's far more efficient to set up an IBOutlet for the constraint, and edit the constant property for the constraint directly, so that's what I've done. So I set up the view controller to be the delegate of the text view, and wrote a textViewDidChange that updated the text view's height constraint:
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
self.textViewHeightConstraint.constant = textView.contentSize.height;
[self.scrollView layoutIfNeeded];
}
Note, my text view has two height constraints, a mandatory minimum height constraint, and a medium priority constraint that I change above based upon the amount of text. The main point is that it illustrates a practical example of changing constraints programmatically. You shouldn't have to muck around with the scrollview's bottom constraint at all, but this is shows a real-world example of when you might want to adjust a constraint.
When you add a scrollview in IB, it will automatically get all the constraints you need. You probably don't want to be adding a constraint programmatically (at least not without removing the existing bottom constraint).
Two approaches might be simpler:
Create an IBOutlet for your existing bottom constraint, say scrollViewBottomConstraint. Then you can just do
self.scrollViewBottomConstraint.constant = 0.0;
Or create your view initially in IB where the bottom constraint is 0.0 and then you don't have to do anything programmatically at all. If you want to layout a long scrollview and it's subviews, select the controller, set it's simulated metrics from "inferred" to "free form". Then you can change the size of the view, set the scrollview's top and bottom constraints to be zero, layout everything you want inside the scroll view, and then when the view is presented at runtime, the view will be resized appropriately, and because you've defined the scrollview's top and bottom constraints to be 0.0, it will be resized properly. It looks a bit odd in IB, but it works like a charm when the app runs.
If you're determined to add a new constraint, you could either programmatically remove the old bottom constraint, or set the old bottom constraints' priority down as low as possible, and that way your new constraint (with higher priority) will take precedence, and the old, low-priority bottom constraint will gracefully not be applied.
But you definitely don't want to just add a new constraint.
It's possible to create outlets to represent layout constraints in your view controller. Just select the constraint you want in interface builder (e.g. via "select and edit" on the measurements pane of the view you are arranging). Then go to the outlets pane and drag a "New Referencing Outlet" to your code file (.h or .m). This will bind the constraint to an NSLayoutConstraint instance that you can access from your controller and adjust dynamically on the fly (generally via the constant property, which is poorly named because it's not a constant at all).
(Note that in XCode 6 you can double-click the constraint to select it for editing.)
Be careful when adjusting the layout in interface builder, however, as you may end up deleting the constraint and have to re-bind it to the outlet.
Looking at the console information, i feel that you are creating ambiguity when you add two same type of constraint.
So instead of creating and adding new constraint, try updating the previous constraint that is already in the constraints array.
for(NSLayoutConstraint *constraint in self.view.constraints)
{
if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom &&
constraint.firstItem == self.view && constraint.secondItem == self.scrollView)
{
constraint.constant = 0.0;
}
}
Hope this helps
Even Rob's answer will work!
You can use https://github.com/SnapKit/Masonry for adding constraints programmatically.
It is power of AutoLayout NSLayoutConstraints with a simplified, chainable and expressive syntax. Supports iOS and OSX Auto Layout.
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:#[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],]];
in just few lines
Heres the same constraints created using MASConstraintMaker
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
Or even shorter
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
do your best is sort ;)