I know a lot of questions have been asked about it but could not figure out.
I have a label that needs its number of lines adusting dynamically. It works with automatic preferred max width, but could not make it work when explicit (I want to support ios7).
The label height does not currently increase
Here is my code:
-(void)viewDidLoad
{
[super viewDidLoad];
_feedBackLabel.numberOfLines = 0;
_feedBackLabel.lineBreakMode = NSLineBreakByWordWrapping;
_feedBackLabel.preferredMaxLayoutWidth = self.view.frame.size.width - 90;
[_feedBackLabel setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisVertical];
[_feedBackLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
}
-(void)setFeedback:(NSString*)response
{
self.feedBackLabel.text = response;
self.feedBackLabel.hidden = NO;
[self.feedBackLabel sizeToFit];
}
I have pinned my label with a leading and a trailing space to its superview with priority required.
The content hugging and compression resistance are both 750 in the vertical axis.
The label has a height constraint with a priority of 500.
Thanks
Guillaume
Remove height constraint from label and run your code
Actually it was working partially. It was only working for long strings; if the string was just a bit too big for my label then it did not work.
What I did is increase my horizontal content hugging priority to 750, i.e. higher than the height priority which is 500 (all other constraints are required), and set the preferredMaxLayoutWidth dynamically, and it works as expected now.
Thanks
Guillaume
Related
I have a UITableViewCell that has two columns in it. Each column is a UILabel and each label is multiline (numberOfLines = 0).
What I would like is to have the table view cell vertically sized based on whichever label is taller. I have constraints setup for the left right and top of each label, but I am not sure how to add the bottom constraints since it needs to be a constraint on the tallest label.
Here is what I have right now:
But this is what I am trying to achieve. But either the left or the right column could be taller. In the picture, the right column is taller, but it just as well could be the left column depending on the data supplied to it.
I have thought about adding a height constraint to constraint both labels to the same height and then adding the bottom constraint from there, but then the shorter label will not be vertically aligned, or I do not know of a way of aligning them vertically without subclassing UILabel or using UITextView, which I would prefer to not do if possible.
Is there a good way to have the table view cell be able to vertically auto-size based on whichever column is taller? Thanks for your help.
Update
I have added two additional constraints based on the answer provided. But for some reason I still cannot get it to autosize the table cell. I have the row height set to automatic in Interface Builder. Here are the constraints I have configured currently.
Is there anything in the constraints that would prevent the table view cell from increasing in height to match the height of the labels?
I am not sure if this is the problem or not, but I tried to add a low priority height constraint to the content view also, as was suggested, but I am unable to add the constraint or I do not know how to do that. I can add a height constraint to other views, but not to the content view of the table view cell.
Update 2
Here are the constraints in code. This is in a UITableViewCell subclass and this code runs as part of the initialization of the cell.
[self addSubview:self.firstLabel];
[self addSubview:self.secondLabel];
NSLayoutConstraint *heightConstraint = [self.heightAnchor constraintEqualToConstant:1.0f];
[heightConstraint setPriority:50];
[NSLayoutConstraint activateConstraints:#[
[self.firstLabel.leadingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.leadingAnchor],
[self.firstLabel.topAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.topAnchor constant:0.0f],
[self.firstLabel.trailingAnchor constraintEqualToAnchor:self.centerXAnchor constant:-4.0f],
[self.secondLabel.leadingAnchor constraintEqualToAnchor:self.centerXAnchor constant:4.0f],
[self.secondLabel.firstBaselineAnchor constraintEqualToAnchor:self.firstLabel.firstBaselineAnchor],
[self.secondLabel.trailingAnchor constraintEqualToAnchor:self.contentView.layoutMarginsGuide.trailingAnchor],
[self.contentView.heightAnchor constraintGreaterThanOrEqualToAnchor:self.firstLabel.heightAnchor constant:8.0f],
[self.contentView.heightAnchor constraintGreaterThanOrEqualToAnchor:self.secondLabel.heightAnchor constant:8.0f],
heightConstraint
]];
Here is what it looks like when run on the device. The labels are all short, except the first one, which is supposed to span several lines. But for some reason, it is being truncated even though I have the number of lines set to 0 and I think the content hugging and content compression resistance priorities set to what I think should be correct.
Here are how my labels are defined:
- (UILabel *)firstLabel {
if (!self->_firstLabel) {
self->_firstLabel = [[UILabel alloc] init];
self->_firstLabel.translatesAutoresizingMaskIntoConstraints = NO;
self->_firstLabel.numberOfLines = 0;
self->_firstLabel.userInteractionEnabled = NO;
self->_firstLabel.contentMode = UIViewContentModeScaleToFill;
[self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
[self->_firstLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
[self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self->_firstLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
self->_firstLabel.textAlignment = NSTextAlignmentNatural;
self->_firstLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self->_firstLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
self->_firstLabel.adjustsFontSizeToFitWidth = NO;
//TODO: remove this
self->_firstLabel.backgroundColor = [UIColor orangeColor];
}
return self->_firstLabel;
}
- (UILabel *)secondLabel {
if (!self->_secondLabel) {
self->_secondLabel = [[UILabel alloc] init];
self->_secondLabel.translatesAutoresizingMaskIntoConstraints = NO;
self->_secondLabel.numberOfLines = 0;
self->_secondLabel.userInteractionEnabled = NO;
self->_secondLabel.contentMode = UIViewContentModeScaleToFill;
[self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
[self->_secondLabel setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];
[self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self->_secondLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
self->_secondLabel.textAlignment = NSTextAlignmentNatural;
self->_secondLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self->_secondLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
self->_secondLabel.adjustsFontSizeToFitWidth = NO;
//TODO: remove this
self->_secondLabel.backgroundColor = [UIColor yellowColor];
}
return self->_secondLabel;
}
Yes, it's pretty straightforward when you understand about constraint inequalities and priorities. Here, the label on the left is longer:
Here, the label on the right is longer:
The yellow view, which stands in for a table view cell here, is sized to the larger of the labels.
How is that done? In addition to the labels being pinned top, left, and right in the normal way, the superview (yellow view) bottom has two greater-than-or-equal constraints, one to the bottom of each label; and it is itself given a very small height at a very low priority (as a way of telling the layout engine to make the height as small as possible while still obeying the inequalities, which would otherwise be ambiguous).
EDIT There appears to be some doubt that this would work for an actual table view, so here's proof that it does.
I am trying to position a UILabel instance label next to a UITextField instance textfield like done in the iOS Settings dialogs. I'm using Auto Layout and constraints with the help of Florian Kuglers FLKAutoLayout extensions (https://github.com/dkduck/FLKAutoLayout).
When I set only a leading constraint for the label and a space constraint between label and textfield, the label and textfield widths are adjusted to their content. (bottom picture)
But when I set also a trailing constraint for the textfield, only textfields width is adjusted to its content, but label is stretched. (top picture)
I want to behave the opposite around, so that the textfield will be stretched and the label is adjusted to its content. Why does iOS decide to stretch the label instead of the textfield?
[self addSubview:self.label];
[self addSubview:self.textField];
[self.label alignLeadingEdgeWithView:self predicate:#"10"];
[self.textField constrainLeadingSpaceToView:self.label predicate:#"10"];
// difference between top and bottom pciture
// [self.textField alignTrailingEdgeWithView:self predicate:#"-25"];
EDITED: So after the comment of Rahul I played with width and priorities. Adding the following width constraint lead into the somewhat wrong direction, because of entering some very long value, the label was resized too small.
[self.label constrainWidth:#"0#1"];
So I finally read up on "Compression Resistance and Content Hugging" (http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html) and the solution is to set the Horizontal Content Hugging priority of the label to a higher value.
[self.label setContentHuggingPriority:500 forAxis:UILayoutConstraintAxisHorizontal];
Then even entering big values will let the label stay as width as its content!
Solution is not adding a width constraint with priority, but to set Compression Resistance and Content Hugging properties for the intrinsic content size of the UIViews.
Following constraints which I set in the storyboard works exactly how you want.
Horizontal space constraint between label and superview.(default)
Horizontal space constraint between textfield and superview. (default)
Horizontal space constraint between textfield and label.
I have a UILabel and I want to show some text in this label. I want to increase the label width at most 70% of the full screen of my device. If text length of that label doesn't fit this 70% of size then the label automatically goes to the next line as long as the text length. Every time the label length cross the 70% width of main screen then lines break as well. I have tried several ways but unable to solve yet. Please help me to solve this.
Thanks in advance;
Drag a label to your storyboard and add top and leading constraints to it.
Now select the label and control drag to the view holding the label (in your case view of ViewController) you will see the pop up and then select equal width
Now your Label's width is equal to your view's width :) That's not you want you want ur label width to be 70% of your view. So select the equal constraint of label, go to property inspector and change the multiplier to 0.7
Now your label width is 70% of your view!
But you don't want it to be 70% always. It can be at max 70% of screen, so
now change the relationship of constraint from being equal to less than or equal to.
select label and change number of lines to 0.
That's it :) have fun :)
Sample O/P:
When text is short - vs - long:
- - -
EDIT:
Not using a storyboard? Not a problem; write the same constraint programmatically and apply it to label simple enough. If you need help lemme know :)
EDIT:
As you have specified that you want to leave the gap at the beginning of each line in label you can achieve it by using Edge insets
- (void)drawTextInRect:(CGRect)rect {
UIEdgeInsets insets = {0, 5, 0, 0};
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
You must have forgotten to increase the label's height.
The code below is for allowing the UILabel to have multiple lines:
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
Then you have to make sure the UILabel's frame has enough height to show the lines. You can achieve this by calculating the required height for the given text (NSString):
NSString *text = #"YourText";
CGFloat your70Width; // whatever your width is
CGSize constraintSize = CGSizeMake(your70Width, MAXFLOAT);
UIFont *yourLabelFont; // whatever your font is
CGRect requiredFrame = [text boundingRectWithSize:constraintSize options:NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName:yourLabelFont} context:nil];
// Keeps the old x,y coordinates and replaces only the width and height.
CGRect oldLabelFrame = label.frame;
label.frame = CGRectMake(oldLabelFrame.origin.x, oldLabelFrame.origin.y, requiredFrame.size.width, requiredFrame.size.height);
Now the label will be shown nicely in multiple lines.
To increase the height of the label according to the content if you are using storyboard. Give the label FOUR constraints (Top, Bottom, Leading, Trailing) then go to Attribute Inspector and make lines to 0 and in line break do it WORD WRAP.
I'm trying to use autolayout to automatically resize my popover to fit it's contents. I have fixed popover width but to compute height i rely on systemLayoutSizeFittingSize: passing my predefined width and zero height e.g. CGSizeMake(190, 0).
ContentController* controller = [ContentController new];
CGSize preferredSize = [controller.view systemLayoutSizeFittingSize:CGSizeMake(190, 0)];
controller.preferredContentSize = preferredSize;
UIPopoverController* popover = [[UIPopoverController alloc] initWithContentViewController:controller];
//popover presentation.
So far, so well, my current ContentController view hierarchy is something like (setup in the Interface Builder):
UILabel - multiline header (dynamically resized)
|
UIImage with fixed width / height (static size)
|
UILabel - multiline body (dynamically resized)
Thus, i just plug in my header / body text, call systemLayoutSizeFittingSize and it returns valid size that fits all the content of the view.
The problem arises when i try to put my body label inside UIScrollView. (Both in the IB and in code).
From now on, systemLayoutSizeFittingSize will not take body label height into account and will return height for header + image.
I've setup all the constraints in the IB to support Pure Autolayout approach.
I've checked scrollview's content size - it is indeed equals to body label's intrinsic size, but scroll view's frame is squashed into 0.
I've checked and tried to reset label's maxPreferredLayoutWidth to the width of the content view, but it doesn't seems to affect anything.
I've set translatesAutoresizingMaskIntoConstraints on every view to NO, but it has no effect.
I've set hugging / compression resistance priorities of both label and it's scrollview to 1000, but no luck. Setting them on the container view doesn't work either.
Here are screenshots of my IB view setup:
My guess that is is somehow related to popover hosting views and their autolayout constraints, but i'm not sure.
I'm updating my labels via simple
_textContainerHeaderLabel.text = headerText;
_textContainerBodyTextLabel.text = bodyText;
[self.view setNeedsUpdateConstraints];
[self.view layoutSubviews];
So, the main question - how do i compute view's optimal fitting size via autolayout when it has UIScrollViews in it?
Finally found a solution for this case. Don't know how correct it is, but i did the following - I've added height inequality constraint (>=0) from label to it's scrollview.
The trick is to make this constraint's priority lower than label's compression resistance (vertical, in my case). This seems to to solve this problem.
I'm trying to programmatically create a container view with two UILabel subviews which behave as follows:
The container width is pinned to its superview; its height is constrained to fit the labels
The labels are laid out horizontally, with standard spacing between them (8pts)
The left label width is 25% of the width of the container
The right label width fills the available space, minus standard horizontal spacing
Long text should be broken at word boundaries are flow across multiple lines; both labels must grow vertically to accommodate long text
I have defined the labels with numberOfLines = 0 and lineBreakMode = NSLineBreakByWordWrapping.
Note that the size of the container is completely dynamic; its width is determined by its superview, while its height is determined by its subviews (the labels). The size of the labels is also dynamic; their widths are proportional to the container width, and their heights depend on the length of the text.
I've been able to achieve everything above, except for the last item, with the following constraints (pseudo-code). A is the left label, B is the right.
A.top == container.top
B.top == container.top
A.leading = container.leading
A.trailing == B.leading - 8
B.trailing == container.trailing
A == .25 * container.width
container.height >= A.height
container.height >= B.height
The last 2 constraints are intended to stretch the container to fit the taller of the labels, but the layout engine seems to ignore the fact that the labels may be multiline. That is, I always get a single line displayed, no matter the length of the text.
So what constraints do I need to add/modify/delete in order to achieve the full set of behaviors described above?
To make your labels automatically resize height you need to do following:
Set layout constrains for label (That what you actually have done)
Set height constraint with low priority. It should be lover than ContentCompressionResistancePriority
Set numberOfLines = 0
Set ContentHuggingPriority higher than label's hight priority
Set preferredMaxLayoutWidth for label. That value is used by label to calculate its height
For example:
self.descriptionLabel = [[[UILabel alloc] init] autorelease];
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_)]];
Set the priority of Height Constraints for the labels to low value and try setting the constraints in code.
Make sure you set
horizontal and vertical content compression resistance priority. If you do not want label to truncate its content then set it to 1000.i.e. required.
Content hugging priority. Look at this answer to understand how it works.