Get same result with NSLayoutConstraint as with Autosizing - ios

In my UI I have 5 buttons at the bottom. With autosizing applied to every button like on the picutre:
I get desired results:
However, when I tried to do it with Autolayout in IB or in code like this:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_button1, _button2, _button3, _button4, _button5);
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"|-[_button1]-[_button2]-[_button3]-[_button4]-[_button5]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:nil
views:viewsDictionary];
[self.view addConstraints:constraints];
I get this:
Even when I try to set default width, I don't get behaviour I expected.

You need to set the equal width constraint to all the buttons and set the horizontal space constraint between each buttons through IB.
To set equal height
* Select all buttons
* Editor -> Pin -> Width equally
Hope this works well, as it worked for me.

#"|-[_button1]-[_button2(==_button1)]-[_button3(==_button1)]-[_button4(==_button1)]-[_button5(==_button1)]-|"
Gets you all equal width buttons, although they will stretch to fill the space in landscape...

Related

Superview not increasing in height based on the subviews constraint

I have a scrollview and a separate UIView where I placed a series of textFields and labels with constraints which fully occupies the top and bottom. I'm trying to adjust the UIView's height based on its subview constraints but it won't. What is happening is that the view keeps its height and force other textfields to collapse or shrink thus breaking the constraints.
Details
Each subview priority values :
compression = 750
hugging = 250
UIView priority values:
compression = 249
hugging = 749 Set to be lower than the rest.
Most of the textfields has aspect ratio constraint. This causes the field to adjust.
Each subview has vertical/top/bottom spacing between each other. The top and bottom elements has top and bottom constraints to the view as well.
What's on my code:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
/* I had to adjust the UIView's width to fill the entire self.view.*/
if(![contentView isDescendantOfView:detailsScrollView]){
CGRect r = contentView.frame;
r.size.width = self.view.frame.size.width;
contentView.frame = r;
[detailsScrollView addSubview:contentView];
}
}
Screenshots
The view
This is what currently happens. In this instance it forces the email field to shrink. If I place a height value on it, it does not shrink but the layout engine finds another element to break
Edit:
Solved
Maybe I just needed some break to freshen up a bit. I did tried using constraints before but got no luck. However thanks to the suggestion I went back setting the constraints instead of setting the frame on this one and got it finally working.
Solution:
-(void)viewDidLoad{
[detailsScrollView addSubview:contentView];
[contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[detailsScrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView,detailsScrollView);
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views:viewsDictionary];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:viewsDictionary];
NSArray *widthConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView(==detailsScrollView)]-0-|" options:0 metrics:nil views:viewsDictionary];
}
When you use interface builder to deal with the UIScrollView and its child UIView. usually a top, bottom, left and equal width constraints are set between the UIScrollView and its child which is the contentView in your case.
Without those constraints the other option is to set the content size of the UIScrollView. which was the way of using the UIScrollView before introducing constraints.
So, 1. you should add those constraints programmatically.
By using the constraints, the views frame is no longer needed to resize the views.
So, 2. remove frame setting for your content view.
I am not so happy with the way you set the frame in the viewDidLayoutMethod. if I am going to do that here I would take the frame setting out of the if statement.
The code would be as follow with no if statement:
[detailsScrollView addSubview:contentView];
// then set the constraints here after adding the subview.
Put this code anywhere but not inside your viewDidLayoutSubviews method. it will be a bigger problem than setting the frame in there inside if statement.
Note: Originally, if you are going to set frame in the viewDidLayoutSubviews
method. you should do it for all cases. for example for the if case
and the else case. because, next time this method is going to be
called the views will respond to the constraint. and lose its frame.
Another observation: if you want the view to response to its subviews constraint why you need to set the frame for it? right?
After adding the constraint you may need to call the method constraintNeedsUpdate or another related method.

TableviewCell Auto layout with two equal width labels

I am new to Auto layouts, i am trying to design my view with Tableview with Tableview Cell and two Labels. But i am not reach with design similar in iPhone and iPad.
Can any one help to me Please.
Thanks in advance.
You can try:
NSDictionary *views = #{#"label1":self.label1, #"label2":self.label2};
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[label1][label2(==label1)]|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil
views:views]];
The key is [label1][label2(==label1)].
Please add proper constraints to your views. like following
Add pin width and height equal
Leading space for 1st view
Trailing space for 2nd view.
Top/bottom space for both view.
Horizontal/Vertical Spacing

UIView with UILabel subview - responding to AutoLayout changes

I've got a UIView with a UILabel subview that has constraints defined like so:
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[_messageLabel]-50-|" options:0 metrics:nil views:views];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[_messageLabel]-0-|" options:0 metrics:nil views:views];
[self addConstraints:constraints];
In certain instances, the label is not big enough to show all the text, so it is truncated.
When I adjust the size of the UIView within an animation block, the label animates its change in size as appropriate. However, the re-drawing of the text within it kind of 'jumps' -- and fair enough, i wouldn't expect the label to animate a change in the internal drawing of its text.
Anyway, what I'd like to do is fade out this label and perhaps fade in a second to avoid this jerkiness.
My question: Is there a good callback on UIView as to when it will respond to an auto layout change? or is that simply done in layoutSubviews?

How can I arrange these three UILabels using auto layout programmatically?

I have a UIView that has three UILabels on it. I have a title, subtitle, and subtitleDescription, all UILabel properties. I want my title on the top left, the subtitle below the title no gap, and the subtitleDescription to go to the right of the subtitle no gap, aligning the baseline with the subtitle baseline. I want elipses if the view, or views in the case of the subtitle/subtitleDescription. I Would like to use auto layout programmatically.
Similar to this:
_________________________________
|[title] |
|[subtitle][subtitleDescription] |
|________________________________|
I want the labels to go to the upper left hand side rather than centering. In my code right now, it is all centered and all the labels are on top of each other.
I just call sizeToFit on all the UILabel's, other than that I don't adjust the frame at all. Of course this code is after I alloc and init the labels and set the text. Here is my code:
- (void)setup
{
[self.title sizeToFit];
[self.subtitle sizeToFit];
[self.subtitleDescription sizeToFit];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[title]-(>=0)-|"
options:kNilOptions
metrics:nil
views:#{ #"title" : self.title }]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[subtitle]-5-[subdesc]-(>=0)-|"
options:NSLayoutFormatDirectionLeftToRight
metrics:nil
views:#{ #"subtitle" : self.subtitle,
#"subdesc" : self.subtitleDescription }]];
// compR > compR
[self setContentCompressionResistancePriority:900 forAxis:UILayoutConstraintAxisHorizontal];
[self setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[title]-5-[subtitle]|"
options:kNilOptions
metrics:nil
views:#{ #"title" : self.title,
#"subtitle" : self.subtitle }]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.subtitleDescription
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:self.subtitle
attribute:NSLayoutAttributeBaseline
multiplier:1.0
constant:0]];
}
Thank you!!!
Here is a screen shot of the view:
Update
As jrturton pointed out to me, it looks like all my constraints are being broken from the constraints exceptions. I am looking to figure out why they are broken. The message given is "Unable to simultaneously satisfy constraints".
First of all, make sure your labels are autolayout-enabled by setting translatesAutoResizingMaskIntoConstraints = NO.
You don't need any of the sizeToFit calls. At the point of adding constraints, they are meaningless. The labels will use their intrinsic size at the point of layout.
To prevent centring, simply don't pin to both sides of the superview. So instead of this:
"|[title]-(>=0)-|"
Do this:
"|[title]"
Or indeed this:
"|[title]|"
And set left alignment on the label.
For multiple labels in a line, you'd want this:
"|[subtitle]-5-[subdesc]|"
Passing NSLayoutFormatAlignAllBaseline in the options. You can OR (|) the options together if required. Again, you don't need the inequality spacing. A left aligned label will take as much space as it needs to. You may want to set compression resistance / hugging priorities on the two labels so you have rules on which one is truncated if there isn't enough room to display both values.
You're setting content compression resistance on the view itself, this is meaningless if the view itself is not also subject to auto layout. You're also setting it twice to two different values, I'm not sure what you are hoping to achieve with that.
Your current vertical constraints will, if the superview has a fixed size, cause one or other of the labels to be stretched to fill the remaining size, which will centre the text vertically, that's what labels do when they are too tall.
You can overcome this by not pinning to the bottom:
"V:[title]-5-[subtitle]"
I've written extensively about VFL and auto layout here, with links to other autolayout-related articles.

Using constraints to center an item between two elements

I tried, in interface builder, to position an image between a button and the bottom of the view, and have stay centered in different screen sizes. I could not find a way to do this, so I've tried to accomplish that using the code below, but it's not working. I can get it centered using explicit points, but if use >= it hugs the bottom and all the space is added between the image and button.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(image, button);
NSArray *constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button]->=1-[image]->=1-|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
for (int i = 0; i<constraintsArray.count; i++) {
[self.view addConstraint:constraintsArray[i]];
}
How can I get it to center?
Unfortunately, you can't use the >= like that, but it can be done easily in IB. Just give the image view a spacing constraint to the bottom of the superview, and a vertical spacing constraint to the button -- edit one or the other of these to have the same value as the other. Give the image view a fixed height and width constraint, and make sure that the button has no other vertical constraints (delete it/them if it does).

Resources