Here is the sample project demonstrating the problem
What is it?
Two UIViewControllers
in stacks under UINavigationController
. Each has nothing to do with each other other than presenting from one to another. In both controllers, there is a UILabel
. Each uses Autolayout
. Each label holds arbitrary number of lines label.numberOfLines = 0
.
What works?
Transitioning from viewController A
(root) to viewController B
. B
gets allocated and initiated. B
looks well.
What went wrong?
Transitioning back from B
to A
. At ViewDidDisappear
, label in B
decided it will no longer show more than 1 line even though its numberOfLines
is set at 0. When B
was pushed into the stack, its label only shows 1 liner instead of multiple.
What caused it?
No idea. BUT looking in A
the label numberOfLines
was set to 0. If the line were to be removed, label in B
would not have collapsed.
Questions: But WHY? and I like A
to have multiple lines label, how can I overcome this?
Codes
A
@implementation FirstViewController
{
BugController *_bugController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Bar Button
UIBarButtonItem *helpBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"BUG" style:UIBarButtonItemStylePlain target:self action:@selector(bugTapped)];
[self.navigationItem setLeftBarButtonItem:helpBarButtonItem];
// A sample label
UILabel *someLabel = [[UILabel alloc] init];
[someLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[someLabel setText:@"Tap BUG to see a bunch of text in many lines... tap back... then tap BUG again to see that the text has gone to 1 liner.... WTF?"];
[self.view addSubview:someLabel];
// Comment it out to see that problem is fixed
#warning This one liner is a culprit, removing it will make everything normal but WHY?
[someLabel setNumberOfLines:0];
#warning end of warning
//Constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[someLabel]-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(someLabel)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[someLabel]-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(someLabel)]];
}
- (void)bugTapped
{
// Reuse controller
if (!_bugController) {
_bugController = [[BugController alloc] init];
}
[self.navigationController pushViewController:_bugController animated:YES];
}
B
@implementation BugController
- (void)viewDidLoad
{
[super viewDidLoad];
// Setting up stuff
UILabel *header = [[UILabel alloc] init];
[header setTranslatesAutoresizingMaskIntoConstraints:NO];
[header setText:@"This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. "];
[header setNumberOfLines:0];
[self.view addSubview:header];
// Constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[header]-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(header)]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:header attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
}
Do check it out in the sample code link above!
Update 1 with workaround! After working with the code provided by Misha Vyrko, I realized that setting preferredMaxLayoutWidth to non-zero overcomes the bug in the UILabel.
Non broken sample project
Added to the BugViewController
// Constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[header]-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(header)]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:header attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
[header setPreferredMaxLayoutWidth:1]; // NEWLY ADDED
I am still looking for an explanation to why this happens and a better way if there is any.
Update 2 with corrections By setting PreferredMaxLayoutWidth to 1, the label.frame.size.height
actually expands to the height expected, if the width were actually 1. This means if you have any constraints dependency on the height of your label, it will not work. You will need to explicitly set it to the estimate width. It will not handle rotation without aids so watch out for that!
In bugController you haven't got enough constraints to layout label if you set up all constraints all will be ok
@interface BugController ()
@property (nonatomic, strong) UILabel *header;
@end
@implementation BugController
- (void)viewDidLoad
{
[super viewDidLoad];
// Setting up stuff
self.header = [[UILabel alloc] init];
[self.header setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.header setText:@"This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing. This is a multiple line thing! "];
[self.header setBackgroundColor:[UIColor redColor]];
[self.header setNumberOfLines:0];
[self.view addSubview:self.header ];
// Constraints
// [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[header]-|"
// options:0
// metrics:0
// views:NSDictionaryOfVariableBindings(header)]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.header
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.header
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.header
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0]];
self.header.preferredMaxLayoutWidth = self.view.frame.size.width;
[self.view needsUpdateConstraints];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
self.header.preferredMaxLayoutWidth = self.view.frame.size.width;
[self.view needsUpdateConstraints];
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With