I am trying to create a view that contains one image view with 2 labels and text views. The contents of each element will be read in from an SQLite database, so I would like to use Xcode 5's auto layout to change the heights of each UI element and the distance between them based on the content of each to use the screen space as efficiently as possible. I have a few conditions that I am trying to enforce via auto layout in the interface builder, but I am not sure how to implement them:
The distance between a label and the text view directly below it will stay the same
The widths of all elements will stay the same and they will all be centered horizontally
Only heights and y values of the elements' frames will change
If the content heights of the text views exceed the amount of screen height they can be displayed in, the image view's height should be decreased to a minimum value, then the heights of the text views should be decreased
The distance between each element should be equal (excluding distances between associated labels and text views)
I would be able to do all of this programmatically, but I would like to use the auto layout so the view can easily adapt to changes in screen size and to prevent bugs. I have very little experience with auto layout and I am having trouble with the complex specifications I need in this situation. To make what I intend to create clearer, here is a screenshot of the .xib file:
Thanks for the help!
http://www.techotopia.com/index.php/Implementing_iOS_6_Auto_Layout_Constraints_in_Code
please reffer this website,
may be got your solution
Take a look Autolayout Visual Format Language. They talk about this in a few of the auto layout WWDC videos from last year.
This isn't complete, but you will need to define the constraints both vertically and horizontally. This is complete but hopefully gives you a ruff idea to get your started.
// Vertical alignment should be centered
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[_imageView]-[_label1]-[_textBlock1]-[label2]-[textBlock2]-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil
views:viewsDictionary];
// Tell the views/text blocks to take the entire width. The labels will be fine centered on them I think
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-[_imageView]-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil
views:viewsDictionary];
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-[_textBlock1]-|"
options:nil
metrics:nil
views:viewsDictionary];
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-[_textBlock2]-|"
options:nil
metrics:nil
views:viewsDictionary];
Related
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'm creating a custom UITableViewCell, but I don't think it makes a difference - my question stands alone.
I'm subclassing one of the existing cell styles, and adding one view. The superclass has a view (a UILabel) that adjusts its vertical positioning within the cell depending on whether or not there is content in another label. If there is content in the second label, the first label is vertically centered between the second label and the top of the cell view.
If the second label doesn't have any content, the first label is vertically centered between the top and the bottom of the cell view.
I like this behavior. I'm trying to add a third label that is horizontally next to the first label. I've used autolayout constraints to pin the new label to be near the first label, like this:
[NSLayoutConstraint constraintsWithVisualFormat:#"[firstLabel]-(5)-[thirdLabel]" options:0 metrics:nil views:views];
This is working fine, but I can't figure out how to do something similar with the vertical position. Ideally, I would "pin" the third label to always be at the same vertical alignment as the first label, no matter what that is, but I don't see how to express that in the visual format language.
As an alternative, figuring out how to replicate the behavior of the first label (adjusting it's vertical alignment based on the presence--or lack of--the second label).
This constraint works to correctly vertically align the new label when the second label is present, but it doesn't do anything if the second label is empty on that particular cell:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[thirdLabel][secondLabel]" options:0 metrics:nil views:views];
How can I express this layout constraint?
Visual Format Language is best used to create constraints aligning items one after the other.
For more complex layouts like the vertical center alignment you are trying to build, you should use NSLayoutConstraint's constraintWithItem:[...]. method.
Here is what you could do:
[NSLayoutConstraint constraintWithItem:thirdLabel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:firstLabel
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
Hope this helps,
I am in a need of having the series of buttons to be evenly placed in superview Horizontally using Auto Layout.
Here, I want to keep the sizes of the subviews same, only the center of the subviews will be placed in such a way that there is equal number of space between them.
Note: I dont want to set the Size of the superview, I want every thing to be Auto Layout-ed.
Please Help,
I am stuck !!
Thanks!!
You can create as many UIView's as you have buttons, and center the buttons inside the views, the views can be aligned back to back, using this code:
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[view1][view2][view3]|"
options:0
metrics:nil
views:views]];
Make sure you first remove existing constraints from the superview using:
[self.view removeConstraints:self.view.constraints];
and in the viewDidLoad turn off auto resizing conversion:
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
I have two labels. I want to be able to move both if one is moved. How do I "attach" them together with NSLayoutConstraints? I can do this in IB, but need to do it in code.
also, what are NSLayoutAttributeBaseline, NSLayoutAttributeLeading, and NSLayoutAttributeTrailing?
EDIT:
centering poweredByLabel (aka label02):
[constraints addObject:[NSLayoutConstraint constraintWithItem:poweredByLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:myImage
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0]];
stack the labels and switch vertically:
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[rememberPasswordSwitch]-10-[rememberPasswordLabel]-10-[versionLabel]-[poweredByLabel]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:nil
views:viewsDictionary]];
which produces the error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse constraint
format: Options mask required views to be aligned on a vertical edge,
which is not allowed for layout that is also vertical.
V:[rememberPasswordSwitch]-10-[rememberPasswordLabel]-10-[versionLabel]-[poweredByLabel]-|...........................................................................................................^'
w/out the NSLayoutFormatAlignAllBaseline option, it runs fine (they stack but are not all centered horizontally).
If you need to do this in code, first create the NSLayoutConstraint(s), then add the constraint(s) to the labels' superview.
There are two ways to create constraints in code. constraintsWithVisualFormat is usually much more concise than constraintWithItem.
// Make label1's NSLayoutAttributeTrailing be the 'standard Aqua space' away from label2's NSLayoutAttributeLeading. Also, vertically align their baselines.
NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[label1]-[label2]" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(label1, label2) ] ;
Then you add the constraint(s) to the labels' superview:
[label1.superview addConstraints:constraints] ; // Use `label1.superview` or your own reference to the label's superview.
The Cocoa Auto Layout Guide is short and easy to follow. Give it a read, and I'd be happy to answer any questions you still have.
Edit 1
The option NSLayoutFormatAlignAllBaseline creates constraints (in addition to those created by the VisualFormat string) that vertically align the baselines of all specified objects. If your VisualFormat string is creating vertical constraints (it starts with "V:"), you don't want to use this option. You'd want to use 0 (which means no options), or an option that creates horizontal constraints, like NSLayoutFormatAlignAllCenterX.
I'm playing around with AutoLayout and am really banging my head against the wall for what seems like it ought to be an extremely simple solution.
I've got a vertical column of controls: 1 label and 3 buttons.
I want the label to be 40 pixels(points) tall and auto-size its width based on the width of its superview (standard spacing on left, top and right).
I want the 3 buttons to line up vertically below that label.
I would like their widths to auto-size just like the label.
I would like their spacing to be standard (aqua?) spacing (8 points, right?).
I would like the 3 buttons to be the same height.
I can get what I want to happen to work, but I keep getting errors in the console at runtime, and I'd like to figure out WHY I'm getting them and HOW to avoid getting them. I've watched the WWDC videos on AutoLayout, and here's what I've tried so far:
UILabel *label = [self Label];
MMGlossyButton *button1 = ...
MMGlossyButton *button2 = ...
MMGlossyButton *button3 = ...
[[self view] addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[label]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label)]];
[[self view] addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[button1]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(new)]];
[[self view] addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[button2]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(existing)]];
[[self view] addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[button3]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(provider)]];
[[self view] addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[label(40)]-[button1(>=25)]-[button2(==button1)]-[button3(==button1)]-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label, button1, button2, button3)]];
So, this works for displaying the view in a dynamically-sized way, but the following error pops up in the console:
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)
// A whole bunch of constraint stuff that doesn't appear to be important...
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7554c40 V:[MMGlossyButton:0x7554b40(99)]>
So, the last bit appears to indicate that the first button I have on the view is statically sized to 99 points tall.
Which is the size it has on the view.
Which is completely arbitrary.
Which I don't want assigned, but can't figure out a way to un-assign it.
Although I'm getting what I want (sort of, eventually), it seems to be a REALLY roundabout way to accomplish something that's pretty simple. Am I missing something basic about AutoLayout, or does its power require such complexity?
You are encountering errors because you are mixing and matching constraints generated in code with constraints added by interface builder. Interface builder doesn't let you generate an ambiguous layout, so almost by definition if you add additional constraints, you will get an "Unable to simultaneously satisfy" error, which is the downfall of many a marriage.
To resolve this you either need to define all the constraints you need in interface builder, or you need to mark specific ones as outlets and remove them in code before adding your own.
In your case, the constraints are simple enough to create in IB.
You can pin to a specific height by using this button in IB while your label is selected:
The one in the middle, that looks like a girder. This gives you the following useful menu:
Choosing one of these allows you to create a new constraint against the label, which you can then edit by selecting it:
You can then add your buttons, select all three of them and, using the same menu, create an equal heights constraint.
The constraints created in IB aren't particularly flexible, so if you do decide you need to create or modify them in code, it is best to create outlets to the particular constraints, and then either remove and re-create them, or modify the constant value at run time.