UILabel sizeToFit doesn't work with autolayout ios6 - ios

How am I supposed to configure programmatically (and in which method) a UILabel whose height depends on its text? I've been trying to set it up using a combination of Storyboard and code, but to no avail. Everyone recommends sizeToFit while setting lineBreakMode and numberOfLines. However, no matter if I put that code in viewDidLoad:, viewDidAppear:, or viewDidLayoutSubviews I can't get it to work. Either I make the box too small for long text and it doesn't grow, or I make it too big and it doesn't shrink.

Please note that in most cases Matt's solution works as expected. But if it doesn't work for you, please, read further.
To make your label automatically resize height you need to do following:
Set layout constrains for label
Set height constraint with low priority. It should be lower than ContentCompressionResistancePriority
Set numberOfLines = 0
Set ContentHuggingPriority higher than label's height priority
Set preferredMaxLayoutWidth for label. That value is used by label to calculate its height
For example:
self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;
[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];
NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:#"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[descriptionLabel_(220#300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
Using Interface Builder
Set up four constraints. The height constraint is mandatory.
Then go to the label's attributes inspector and set number of lines to 0.
Go to the label's size inspector and increase vertical ContentHuggingPriority and vertical ContentCompressionResistancePriority.
Select and edit height constraint.
And decrease height constraint priority.
Enjoy. :)

In iOS 6, using autolayout, if a UILabel's sides (or width) and top are pinned, it will automatically grow and shrink vertically to fit its contents, with no code at all and no messing with its compression resistance or whatever. It is dead simple.
In more complex cases, just set the label's preferredMaxLayoutWidth.
Either way, the right thing happens automatically.

Although the question states programmatically, having encountered the same problem, and preferring to work in Interface Builder, I thought it might be useful to add to the existing answers with an Interface Builder solution.
The first thing is to forget sizeToFit. Auto Layout will handle this on your behalf based upon the intrinsic content size.
The problem therefore is, how to get a label to fit it's content with Auto Layout? Specifically - because the question mentions it - height. Note that the same principles apply to width.
So let's start with an example UILabel that has a height set to 41px high:
As you can see in the screen grab above, "This is my text" has padding above and below. That is padding between the UILabel's height, and it's content, the text.
If we run the app in the simulator, sure enough, we see the same thing:
Now, let's select the UILabel in Interface Builder, and take a look at the default settings in the Size inspector:
Note the highlighted constraint above. That is the Content Hugging Priority. As Erica Sadun describes it in the excellent iOS Auto Layout Demystified, this is:
the way a view prefers to avoid extra padding around it's core content
For us, with the UILabel, the core content is the text.
Here we come to the heart of this basic scenario. We have given our text label two constraints. They conflict. One says "the height must be equal to 41 pixels high". The other says "hug the view to it's content so we don't have any extra padding". In our case, hug the view to it's text so we don't have any extra padding.
Now, with Auto Layout, with two different instructions that say do different things, the runtime has to choose one or the other. It can't do both. The UILabel can't be both 41 pixels high, and have no padding.
The way this is resolved, is by specifying priority. One instruction has to have a higher priority than the other. If both instructions say different things, and have the same priority, an exception will occur.
So let's give it a go. My height constraint has a priority of 1000, which is required. Content hugging height is 250, which is weak. What happens if we reduce the height constraint priority to 249?
Now we can see the magic start to happen. Let's try in the sim:
Awesome! Content hugging achieved. Only because height priority 249 is less than content hugging priority 250. Basically, I'm saying "the height I specify here is less important than what I've specified for the content hugging". So, the content hugging wins.
Bottom line, getting the label to fit the text can be as simple as specifying the height - or width - constraint, and correct setting that priority in association with that axis' content hugging priority constraint.
Will leave doing the equivalent for width as an exercise for the reader!

Noticed in IOS7 sizeToFit wasn't working also - perhaps the solution may help you too
[textView sizeToFit];
[textView layoutIfNeeded];

Another option for ensuring the label's preferredMaxLayoutWidth is kept in sync with the label's width:
#import "MyLabel.h"
#implementation MyLabel
-(void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
// This appears to be needed for iOS 6 which doesn't seem to keep
// label preferredMaxLayoutWidth in sync with its width, which
// means the label won't grow vertically to encompass its text if
// the label's width constraint changes.
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
#end

I feel I should contribute as it took me a while to find the right solution:
The goal is to let Auto Layout do its work without ever calling sizeToFit(), we will do this by specifying the right constraints:
Specify top, bottom, and leading/trailing space constraints on your UILabel
Set the number of lines property to 0
Increment the Content Hugging Priority to 1000
Lower the Content Compression Resistance Priority to 500
On your bottom container constraint, lower the priority to 500
Basically, what happens is that you tell your UILabel that even though it has a fixed height constraint, it can make break the constraint to make itself smaller in order to hug the content (if you have a single line for example), but it cannot break the constraint to make it larger.

In my case I was creating a UIView subclass that contained a UILabel (of unknown length). In iOS7 the code was straightforward: set the constraints, don't worry about content hugging or compression resistance, and everything worked as expected.
But in iOS6 the UILabel was always clipped to a single line. None of the answers above worked for me. Content hugging and compression resistance settings were ignored. The only solution that prevented clipping was to include a preferredMaxLayoutWidth on the label. But I did not know what to set the preferred width to, as the size of its parent view was unknown (indeed, it would be defined by the contents).
I finally found the solution here. Because I was working on a custom view, I could just add the following method to set the preferred layout width after the constraints had been calculated once, and then recalculate them:
- (void)layoutSubviews
{
// Autolayout hack required for iOS6
[super layoutSubviews];
self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
[super layoutSubviews];
}

I added UILabel programmatically and in my case that was enough:
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0

i have solved with xCode6 putting "Preferred Width" to Automatic and pin the label top, leading and trailing

UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));

I ran into this problem as well with a UIView subclass that contains a UILabel as one if its internal elements. Even with autolayout and trying all of the recommended solutions, the label just wouldn't tighten its height to the text. In my case, I only ever want the label to be one line.
Anyway, what worked for me was to add a required height constraint for the UILabel and set it manually to the correct height when intrinsicContentSize is called. If you don't have the UILabel contained in another UIView, you could try subclassing UILabel and provide a similar implementation by first setting the height constraint and then returning
[super instrinsicContentSize]; instead of [self.containerview intrinsiceContentSize]; like I do below which is specific to my UIView sublass.
- (CGSize)intrinsicContentSize
{
CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
return [self.containerView intrinsicContentSize];
}
Works perfectly now on iOS 7 and iOS 8.

A solution that worked for me; If your UILabel has a fixed width, change the constraint from constant = to constant <= in your interface file

In my case when using the labels in a UITableViewCell, the label at would resize but the height would overrun the table cell height. This is what worked for me. I did according to Max MacLeod, and then made sure cell height was set to UITableViewAutomaticDimension.
You can add this is in your init or awakeFromNib,
self.tableView.rowHeight = UITableViewAutomaticDimension.
In the storyboard, select the cell, open the Size inspector, and make sure row height is set to "Default" by uchecking the 'Custom' checkbox.
There is an issue in 8.0 that also requires it to be set in code.

Related

How to Increase/decrease UIButton width according to title lable text changes?

The Question I have asked is purely using auto-layout, I don't want to setup the frames manually according to text width.
I am aware of the changes Content hugging priority and I have used the same for one label and one button it works fine for the label but not for the button, can anyone help
The button already sets its “intrinsic content width” to fit its title.
If no other constraints override that intrinsic content width, the button will be that intrinsic width, which is what you want. Demo:
Here's my storyboard outline:
I set the Alignment of the horizontal stack view to “Left” (instead of “Fill”) so it wouldn't stretch the top row to fill the screen width.
If your button is not at its intrinsic width, you have higher-priority (probably priority 1000) constraints forcing it to some other width.
You can try to force it to always stay at its intrinsic content width by setting both its Content Hugging Priority and its Content Compression Resistance Priority to 1000.
If you set both priorities to 1000, and you have any other required constraints that prevent the button from being its intrinsic size, you will get error messages in the debug log at runtime telling you that you have conflicting (unsatisfiable) constraints. These messages will include the full set of constraints, so you can try to track down what constraints you have that you don't want.
If you want to do this, you can create the custom View: Take - UIView and change it's class to UIButton and Add the UILabel and apply all required constraints.
With this all setup you can achieve the desire UI.
Please try this If you want to implement like this.
UILabel object's width constraint should be a Less Than
or Equal relation
UIButton object's width constraint should be a Greater
Than or Equal relation
UILabel object's horizontal Content Hugging Priority
should be 249 (as per my constraint settings shown above)
And set UILabel width constant value as maximum possible value as your requirement.
And related code as:
[buttonObject setTitle:#"Button title is ..." forState:UIControlStateNormal];
[buttonObject sizeToFit];

IOS Label and View constraints height

I am really confused about constraints is iOS, i've read a lot of articles, but get stuck, when tried to set Height constraints of UIView according to UILabel content height. I know that is the common question, but i really don't understand the solutions. I thought that the main thing in this question is the constraint priority, but i can't set them properly. In one case, the height of UIView wont change to 0, and in other UILabel height does not make any sense.
So. I've got:
UIView with height constraint, and descendants constraints (below)
UIImageView with height=32, width=32, top=10, left=10, bottom>=10
UILabel with left to UIImageView = 10, top=10, right=10, bottom=10
And i want:
If i got any text to place in the UILabel, i want to dynamic height of UIView according to height of content size of UILabel.
And if there is no text to place in UILabel, i want to set the height of UIView equals to 0 (hide the UIView totally).
UIImageView - is just the icon. If there is no text, must be shrink to 0, if there is some text, must have height 32, top constraint 10, bottom constraint more or equals to 10.
UPD:
Fix the problem, by adding height constraint programmatically. (don't like this)
You can make the constraints take full control of the label,image and view heights, if you set the priorities correctelly.
Set the four vertical margins priorities to less than 1000 (lets say 999)
Set the image height priority to 999 too.
Set lable vertical hugging priority to 1000
Set image height less than or equal label height with 1000 priority
Set label height less than or equal container view height with multiplier 1: 10(or any big number) with priority 1000
Like this
The View is this
and this

Allow UILabel to grow dynamically using auto layout

I have a label which is going to contain a big description. I want the label to continue growing on new lines. In the image, its the label which starts with event_venue.....
The even_venue.. label has 3 constraints for now:
Vertical space with eventt_title
a horizantal space with the leading of the superview
a width constraints which defines that the label width is always less than the superview.width.
What I want to acheive is to make the event_venue.width less than superview.width, but if it has more text, it should display in new lines. Is this possible using autolayout?
This are possible steps which can create expandable UILabel
Set layouts for UILabel fixing its position
Set number of lines = 0
Set content vertical compression resistance to 1000 (this will allow text to push the label)
Since you want UILabel to expand you cannot give it fixed height constraint or its parent fixed height constraint. Sometimes depending upon condition giving height constraint is necessary to avoid error then you need to set its priority lower than vertical compression resistance
Yes, this totally is possible. I see answers here that are close to solution but not complete. Here is a solution which works with auto layout in Storyboard, no coding of sizeToFit or anything. Your modified steps would be:
Vertical space with eventt_title
A horizontal space with the leading of the superview
A horizontal space with the trailing of the superview
Set UILabel's Line Breaks as Word Wrap.
Set UILabel's lines property as 0.
I have solved a similar problem. I had to make a label that had a variable amount of text. Here's what I did:
In the storyboard, place your label with the origin where you want it.
In the Attributes Inspector, "Label" section, set the Line Breaks = Word Wrap
Fill the label with random placeholder text to the maximum shape you want. For example, if you wanted to fill the whole width and have room for a maximum of three lines of text, you could do:
abcdefghijklmnopqrstu
abcdefghijklmnopqrstu
abcdefghijklmnopqrstu
In the code, set the text of the label using setText:
[self.myLabel setText:#"MyLabelText"];
This did it for me. Your situation may be a little different in that I wasn't changing the width of the superview and it sounds like you might be. But if the width constraint is set on the label then I would expect this to work in your case, too.
I had a similar question about label resizing, and the answer that I found that was useful to me is here: UILabel Auto Size Label to Fit Text. This is a good starting source for code on how to resize your label programmatically.
I would recommend that you also add a horizontal trailing auto layout constraint from the label to the edge of the superview. Do that and you can then get rid of your current width constraint.
AutoLayout facilitate you for orientation purpose. I don think it will give you automatic expansion. You have to define label with width and height completely, otherwise you will see dots at the end of label. So you may use UITextView expanding it all over the screen. And set textView.backgroundcolot = clearColor.

Auto sizing UILabel when using Autolayout

I'm creating a xib using autolayout (NSLayoutConsraint). Is there a way to use sizeToFit or something similar to make a UILabel fit its contents?
I've tried setting a constraint of "greater than or equal to x" and tried setting the height to be below this. However, it keeps overwriting the height property. sitToFit doesn't seem to work when you have NSLayoutConstraints.
How can I get the label to size appropriately?
Make an IBOutlet for your width and height constraints, and use sizeToFit to change their constant property, and call needsUpdateConstraints
Using a "greater than constraint" for the height of the UILabel and
calling sizeToFit within viewDidLayoutSubviews did the trick for me
I did not need to call needsUpdateConstraints.
You also need to set the number of lines to 0 to allow an unlimited amount of lines or > 1 if you want a limit.
Also the property preferredMaxLayoutWidth needs to be set, which is automatically set to the views width by the interface builder.
You should try to lower your height constraint priority.

iOS: Multi-line UILabel in Auto Layout

I'm having trouble trying to achieve some very basic layout behavior with Auto Layout. My view controller looks like this in IB:
The top label is the title label, I don't know how many lines it will be. I need the title label to display all lines of text. I also need the other two labels and the small image to be laid out right below the title, however tall it happens to be. I have set vertical spacing constraints between the labels and small image, as well as a top spacing constraint between the title label and its superview and a bottom spacing constraint between the small image and its superview. The white UIView has no height constraint, so it should stretch vertically to contain its subviews. I have set the number of lines for the title label to 0.
How can I get the title label to resize to fit the number of lines required by the string? My understanding is that I can't use setFrame methods because I'm using Auto Layout. And I have to use Auto Layout because I need those other views to stay below the title label (hence the constraints).
How can I make this happen?
Use -setPreferredMaxLayoutWidth on the UILabel and autolayout should handle the rest.
[label setPreferredMaxLayoutWidth:200.0];
See the UILabel documentation on preferredMaxLayoutWidth.
Update:
Only need to set the height constraint in storyboard to Greater than or equal to, no need to setPreferredMaxLayoutWidth.
Expand your label set number of lines to 0 and also more importantly for auto layout set height to >= x. Auto layout will do the rest. You may also contain your other elements based on previous element to correctly position then.
Source: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html
Intrinsic Content Size of Multi-Line Text
The intrinsic content size of UILabel and NSTextField is ambiguous for multi-line text. The height of the text depends on the width of the lines, which is yet to be determined when solving the constraints. In order to solve this problem, both classes have a new property called preferredMaxLayoutWidth, which specifies the maximum line width for calculating the intrinsic content size.
Since we usually don’t know this value in advance, we need to take a two-step approach to get this right. First we let Auto Layout do its work, and then we use the resulting frame in the layout pass to update the preferred maximum width and trigger layout again.
- (void)layoutSubviews
{
[super layoutSubviews];
myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
[super layoutSubviews];
}
The first call to [super layoutSubviews] is necessary for the label to get its frame set, while the second call is necessary to update the layout after the change. If we omit the second call we get a NSInternalInconsistencyException error, because we’ve made changes in the layout pass which require updating the constraints, but we didn’t trigger layout again.
We can also do this in a label subclass itself:
#implementation MyLabel
- (void)layoutSubviews
{
self.preferredMaxLayoutWidth = self.frame.size.width;
[super layoutSubviews];
}
#end
In this case, we don’t need to call [super layoutSubviews] first, because when layoutSubviews gets called, we already have a frame on the label itself.
To make this adjustment from the view controller level, we hook into viewDidLayoutSubviews. At this point the frames of the first Auto Layout pass are already set and we can use them to set the preferred maximum width.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
[self.view layoutIfNeeded];
}
Lastly, make sure that you don’t have an explicit height constraint on the label that has a higher priority than the label’s content compression resistance priority. Otherwise it will trump the calculated height of the content. Make sure to check all the constraints that can affect label's height.
I was just fighting with this exact scenario, but with quite a few more views that needed to resize and move down as necessary. It was driving me nuts, but I finally figured it out.
Here's the key: Interface Builder likes to throw in extra constraints as you add and move views and you may not notice. In my case, I had a view half way down that had an extra constraint that specified the size between it and its superview, basically pinning it to that point. That meant that nothing above it could resize larger because it would go against that constraint.
An easy way to tell if this is the case is by trying to resize the label manually. Does IB let you grow it? If it does, do the labels below move as you expect? Make sure you have both of these checked before you resize to see how your constraints will move your views:
If the view is stuck, follow the views that are below it and make sure one of them doesn't have a top space to superview constraint. Then just make sure your number of lines option for the label is set to 0 and it should take care of the rest.
I find you need the following:
A top constraint
A leading constraint (eg left side)
A trailing constraint (eg right side)
Set content hugging priority, horizontal to low, so it'll fill the given space if the text is short.
Set content compression resistance, horizontal to low, so it'll wrap instead of try to become wider.
Set the number of lines to 0.
Set the line break mode to word wrap.
None of the different solutions found in the many topics on the subject worked perfectly for my case (x dynamic multiline labels in dynamic table view cells) .
I found a way to do it :
After having set the constraints on your label and set its multiline property to 0, make a subclass of UILabel ; I called mine AutoLayoutLabel :
#implementation AutoLayoutLabel
- (void)layoutSubviews{
[self setNeedsUpdateConstraints];
[super layoutSubviews];
self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}
#end
I have a UITableViewCell which has a text wrap label. I worked text wrapping as follows.
1) Set UILabel constraints as follows.
2) Set no. of lines to 0.
3) Added UILabel height constraint to UITableViewCell.
#IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!
4) On UITableViewCell:
priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
One way to do this...
As text length increases try to change (decrease) the fontsize of the label text using
Label.adjustsFontSizeToFitWidth = YES;

Resources