Reputation: 115
I have been searching for the whole day, but could not make it work.
I have a UIView
with two labels, on above the other, on the left side, and one button on the right side. I added the constraints to let autolayout
resize views accordingly. Everything was working perfectly when I had set one constraint for the height of the UIView
(and no one constraint for the height of the two UILabel
), but as the content of the lower UILabel
will vary, I removed that constraint and set two constraints for the UILabel
, one fixed for the upper UILabel
and one with relation "greater than or equal" for the lower UILabel
, and other one to fix the distance between the lower UILabel
and the UIView
.
It looks like auto layout is not capable of calculate the intrinsicContentSize
of the lower UILabel
, because it never increases its height above 10px, despite the content of the lower UILabel
.
UIView *miView = [[UIView alloc] init];
UILabel *miLabel = [[UILabel alloc] init];
[miLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[miDetailsLabel setNumberOfLines:1];
[miDetailsLabel setText:@"Just one line."];
[miView addSubview:miLabel];
UILabel *miDetailsLabel = [[UILabel alloc] init];
[miDetailsLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[miDetailsLabel setNumberOfLines:0];
[miDetailsLabel setText:@"Enough text to show 3 lines on an IP4, except first of three UILabels on my test code, with no content"];
[miView addSubview:miDetailsLabel];
UIButton *miButton = [[UIButton alloc] init];
[miButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[miView addSubview:miButton];
NSArray *constraint_inner_h = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[label]-(10)-[button(52)]-(0)-|" options:0 metrics:nil views:@{@"label":miLabel, @"button":miButton}];
NSArray *constraint_inner2_h = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[details]-(10)-[button]-(0)-|" options:0 metrics:nil views:@{@"details":miDetailsLabel, @"button":miButton}];
NSArray *constraint_label_v = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(0)-[label(18)]-(2)-[details(>=10)]-(0)-|" options:0 metrics:nil views:@{@"label":miLabel, @"details":miDetailsLabel}];
NSArray *constraint_button_v = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button(22)]" options:0 metrics:nil views:@{@"button":miButton}];
[miView addConstraint:[NSLayoutConstraint constraintWithItem:miButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:miView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[miView addConstraints:constraint_inner_h];
[miView addConstraints:constraint_inner2_h];
[miView addConstraints:constraint_label_v];
[miView addConstraints:constraint_button_v];
I ve put a reduced version of the code.
any ideas of what I am missing?
Thanks
UPDATE : Thanks to Matt advice, I've tried this solution to set the proper value to preferredMawLayoutWidth:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSArray *allKeys = [dictOpcionesTickets allKeys];
for (NSString *key in allKeys) {
NSArray *tmpArray = [dictOpcionesTickets objectForKey:key];
if (![[tmpArray objectAtIndex:0] isEqual:@""]) {
UILabel *tmpLabel = [tmpArray objectAtIndex:3];
tmpLabel.preferredMaxLayoutWidth = tmpLabel.frame.size.width;
NSLog(@"width: %f",tmpLabel.frame.size.width);
}
}
[self.view layoutIfNeeded];
}
To explain the code, as I said, I'm doing it dynamically, so I've created a dictionary with an array with the references to my UILabel
(among other interesting information). When I run the code, I get the next log (with the code parsing 3 UILabel
, first label with no content):
2014-12-28 20:40:42.898 myapp[5419:60b] width: 0.000000
2014-12-28 20:40:42.912 myapp[5419:60b] width: 0.000000
2014-12-28 20:40:43.078 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.080 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.326 myapp[5419:60b] width: 229.000000
2014-12-28 20:40:43.327 myapp[5419:60b] width: 229.000000
But I'm still getting the same result...the UIView is still showing a height equals to the minimum height set by the constraints, not showing the content of the UILabel
.
UPDATE 2 Still fooling around.
I've tested my initial code but simplified on a fresh project in xcode 6, running on an actual iPhone 4 with iOS7, and it worked perfectly without setting preferredMaxLayoutWidth
or subclassing UILabel
, and even without calling layoutIfNeeded
on parent view. But when it comes to the real project (I think it was originally builded in xcode 4 or 5) it does not work:
- (void)addLabelsDinamically {
self.labelFixed.text = @"Below this IB label goes others added dinamically...";
UIButton *miBoton = [[UIButton alloc] init];
miBoton.translatesAutoresizingMaskIntoConstraints = NO;
[miBoton setTitle:@"Hola" forState:UIControlStateNormal];
[self.viewLabels addSubview:miBoton];
[self.viewLabels addConstraint:[NSLayoutConstraint constraintWithItem:miBoton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.viewLabels attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[boton(22)]" options:0 metrics:nil views:@{@"boton":miBoton}]];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[boton(40)]" options:0 metrics:nil views:@{@"boton":miBoton}]];
UILabel *miLabel = [[UILabel alloc] init];
miLabel.translatesAutoresizingMaskIntoConstraints = NO;
miLabel.backgroundColor = [UIColor redColor];
miLabel.numberOfLines = 0;
miLabel.text = @"ñkhnaermgñlkafmbñlkadnlñejtnhalrmvlkamnñnañorenoetñnngñsdbmnñgwn";
[self.viewLabels addSubview:miLabel];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(8)-[label]-(10)-[boton]-(8)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(8)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":self.labelFixed}]];
UIView *pre = miLabel;
miLabel = [[UILabel alloc] init];
miLabel.translatesAutoresizingMaskIntoConstraints = NO;
miLabel.backgroundColor = [UIColor greenColor];
miLabel.numberOfLines = 0;
miLabel.text = @"ñkhnaermgñlkafmbñlkadnlñejtnhalrmvlkamnñnañorenoetñnngñsdbmnñgwn";
[self.viewLabels addSubview:miLabel];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(8)-[label]-(10)-[boton]-(8)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(8)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":pre}]];
}
Ah, on the same screen I have one UILabel
added on IB, with numberOfLines
set to '0', vertical constraint "greater or equal than" and horizontal constraint set in a similar way (but both constraints set on IB)...and it works perfectly...this is driving me nuts!! any help???
Upvotes: 3
Views: 12814
Reputation: 3420
This answer will work in a programmatic situation when you are added the text information dynamically and if this above is on a UICollectionViewCell
or UITableViewCell
.
In these situations you will need to update the layout and constraints on the contentView
, then set the preferedWidth:
-(void) layoutSubviews
{
[super layoutSubviews];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
//locationName.preferredMaxLayoutWidth = locationName.frame.size.width;
//locationDetails.preferredMaxLayoutWidth = locationDetails.frame.size.width;
[super layoutSubviews];
}
This is similar to other answers where u are getting the new frame information and using that for the preferredMaxLayoutWidth then updating the view.
You will need constraints added to those views for the vertical as a start point and the UILabel
need their numberOfLines = 0
On second thoughts setting he preferredMaxLayoutWidth
in the layoutSubViews is poor solution if your width changes, as well as the height. Best to set this else where like the init for that cell. In some ways it might not be needed if you are constraining the width. This in my situation lead to the preferredMaxLayoutWidth
changing each time that cell was set.
Upvotes: -1
Reputation: 115
Well, finally I've found the error, as I stated in the "UPDATE 2", it was very strange that it was working on an UILabel
added via IB, but not with those added dynamically.
So, I looked again to my screen on IB, and found that I have an height constraint with "equal" relation, when I changed it to "greater or equal than" it all started to work!!!! Why I did not notice that the constraint was not "greater or equal than"? Because the view with the constraint was getting larger and larger as I was adding views with labels inside. Is that a bug?
Well, to summarize if someone find this useful. "How to add UILabel
s multi-line which adapts to its content?"
In iOS 6 versions it was a bug with "preferredMaxLayoutWidth
", not getting the width once it was "layouted", and as a result of that programmers must set it via code (the best solution is doing it dynamically, an example of that I put it on my above question).
Fortunately on iOS7 and later, this has been solved, and you can rely only with constraints.
Adding via IB
UILabel
, and set "number of lines" to '0' (this will make the UILabel
use as many lines as it needs).Adding via code (just paste the code posted above):
UILabel *miLabel = [[UILabel alloc] init]; // Initialitze the UILabel
miLabel.translatesAutoresizingMaskIntoConstraints = NO; // Mandatory to disable old way of positioning
miLabel.backgroundColor = [UIColor redColor];
miLabel.numberOfLines = 0; // Mandatory to allow multiline behaviour
miLabel.text = @"ñkhnaergñsdbmnñgwn"; // big test to test
[self.viewLabels addSubview:miLabel]; // Add subview to the parent view
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[label]-(10)-|" options:0 metrics:nil views:@{@"label":miLabel}]]; // horizontal constraint
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(10)-[label]" options:0 metrics:nil views:@{@"label":miLabel}]];
UIView *pre = miLabel;
// Adding a second label to test the separation
miLabel = [[UILabel alloc] init];
miLabel.translatesAutoresizingMaskIntoConstraints = NO;
miLabel.backgroundColor = [UIColor greenColor];
miLabel.numberOfLines = 0;
miLabel.text = @"ñkhnaermgñlkafmbnoetñnngñsdbmnñgwn";
[self.viewLabels addSubview:miLabel];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[label]-(10)-|" options:0 metrics:nil views:@{@"label":miLabel,@"boton":miBoton}]];
[self.viewLabels addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previous]-(10)-[label]" options:0 metrics:nil views:@{@"label":miLabel,@"previous":pre}]];
Thanks to those who has tried to help me!! This site is amazing :)
Upvotes: 4