Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple line UILabel autolayout get collapsed to 1 liner when pop

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!

like image 791
Byte Avatar asked Apr 30 '14 20:04

Byte


1 Answers

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
like image 69
Misha Vyrko Avatar answered Nov 15 '22 13:11

Misha Vyrko