Autolayout adding extra padding on top and bottom of UILabel iPhone 6/6+ when preferredMaxLayoutWidth is set

11,032

Solution 1

So what i ended up doing was to create a subclass for the label and override setBounds: and set the preferredMaxLayoutWidth to its bounds.

- (void)setBounds:(CGRect)bounds {
    [super setBounds:bounds];

    if (self.preferredMaxLayoutWidth != bounds.size.width) {
        self.preferredMaxLayoutWidth = bounds.size.width;
        [self setNeedsUpdateConstraints];
}}

Also, in the UITableViewCell subclass, i override the layoutSubiews

- (void)layoutSubviews {
    [super layoutSubviews];

    [self.contentView layoutIfNeeded];

    // labelsCollection is an IBOutletCollection of my UILabel sublasses
    for (UILabel *label in self.labelsCollection) {
        label.preferredMaxLayoutWidth = label.bounds.size.width;
}}

This works all the time. I think the reason for this odd behavior is that the UILabel is initially set to 280px (320px - padding on either side) in the xib file but during run time, it changes its width to accommodate bigger screens (since I set the leading and trailing constraints it increases the width which changes the 'preferredMaxLayoutWidth' to a bigger value). For some reason UILabel doesn't update its preferredMaxLayoutWidth to the bigger value and causes the white space on top and bottom.

That's my hypothesis.

Hope this helps.

Solution 2

Anyone looking into this question that already is using maxPreferredLayoutWidth (btw you do not have to subclass, just set this in viewDidLayoutSubviews of your VC) check that you are setting it to the width of the correct view.

Mine was not working because I had:

label1.maxPreferredLayoutWidth = label1.frame.width
label2.maxPreferredLayoutWidth = label1.frame.width

When it should have been:

label1.maxPreferredLayoutWidth = label1.frame.width
label2.maxPreferredLayoutWidth = label2.frame.width

Do you see the difference? I did not for 3 hours lol

Solution 3

using this class fixed it for me

import UIKit

class CustomLabel: UILabel {

    override func drawText(in rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            let labelStringSize = stringTextAsNSString.boundingRect(with: CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil).size
            super.drawText(in: CGRect(x: 0, y: 0, width: self.frame.width, height: ceil(labelStringSize.height)))
        } else {
            super.drawText(in: rect)
        }
    }

}
Share:
11,032

Related videos on Youtube

newDeveloper
Author by

newDeveloper

Updated on September 16, 2022

Comments

  • newDeveloper
    newDeveloper over 1 year

    I created a sample project to reproduce this.

    I've a xib file with an UILabel having a fixed top, leading and trailing constraint. I added a minHeight constraint and set the number of lines to 0.

    I set the preferredMaxLayoutWidth to Automatic (checked in the xib file).

    In viewDidLoad, I've this:

    self.myLabel.layer.borderColor = [[UIColor redColor] CGColor];
    self.myLabel.layer.borderWidth = 2.0f;
    
    self.myLabel.text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
    

    And when i run the simulator on iPhone 6 or 6+, I get this:

    enter image description here

    I've no idea where that top and bottom padding is coming from and it's proportional to the amount of characters in the UILabel is showing.

    Is there some magic setting I forgot ? It runs fine on iPhone 4" devices.

    Now if I don't set the preferredMaxLayoutWidth, it doesn't have the extra padding but this breaks my multi-lines in the UILabel. It cuts off the text. I did not use Size-Class.

    Edit:

    So I changed few things on my sample project to match the situation on my real project. I've added a tableView (with top, leading, bottom and trailing constraints set to its parent view). Each cell on the tableView has 4 labels. The top label has a top, leading and trailing constraint to the contentView of the cell and the subsequent labels have a vertical constraint to the label above it. Every label has a heightGreaterThan constraint set and a widthGreaterThan set.

    This is how it looks like without the preferredMaxLayoutWidth set (Notice how labels are capped to 1 line). No preferredMaxLayoutWidth

    With preferredMaxLayoutWidth. Now the UILabel shows the entire content but has a padding on top and bottom. With preferredMaxLayoutWidth but with top and bottom padding

    Edit2: Sample Project: https://www.dropbox.com/s/sdahx9njnsxiv98/MultiLineUILabel.zip?dl=1

    • Michael Gaylord
      Michael Gaylord over 9 years
      I have the exact same problem as you. I've even tried hacking the preferredMaxLayoutWidth by adding a few pixels, but this number is highly dependent on how wide the screen is and renders differently on iPhone 6, iPhone 6+ and iPhone 5. Not to mention iPad. Not sure if it is a bug or if there is some other setting / hack to get it to work.
    • JasonD
      JasonD over 9 years
      I had the same problem as well and I was NOT using auto-layout. The problem only occurred on iPhone 6 and not iPad. No combination of autoresize mask settings made any difference. Eventually I solved the problem by doing a Clean, Restart XCode and Rebuild. Weird, but it resolve the problem where a Clean alone would not. XCode 6.1.1.
    • AndyC
      AndyC about 9 years
      Make sure the label's preferred width is set to Automatic and Explicit isn't checked on the setting panel. This solved the issue for me when using autotlayout.
    • E-Riddie
      E-Riddie over 7 years
      Link is dead, please update your answer
  • newDeveloper
    newDeveloper over 9 years
    So that adds padding at the bottom of the textView. TextView Edit: I should clarify that the height of the textView is dynamically set in the code and it is only 1 line high in the xib.
  • newDeveloper
    newDeveloper over 9 years
    Yes, I've a bottom vertical constraint from the label to the parentView (with less priority than the other ones) and I've the horizontal and vertical content hugging priority set to 1000.
  • Henry T Kirk
    Henry T Kirk over 9 years
    Ok. It almost looks as the height it calculated is for a 4inch device but then the width is stretched for larger device. Can you post sample project?
  • stromdotcom
    stromdotcom over 9 years
    If you know the size of the text ahead of time, you can set a height and width constraint on the label and be sure it will format the way you want. Also now that I'm looking at your edits, do you have Line Breaks set to Word Wrap? It doesn't appear that the text is wrapping the way you want.
  • newDeveloper
    newDeveloper over 9 years
    Sure, dropbox.com/s/sdahx9njnsxiv98/MultiLineUILabel.zip?dl=1 I turned off the preferredMaxLayoutWidth. Turn it on to see multi-line label.
  • newDeveloper
    newDeveloper over 9 years
    That's my last resort. Ideally i would like the autolayout handle it (and it handles it fine on iPhone 4" and smaller screens). Changing the line break makes no change to the actual problem (it changes where it breaks the line or shows the ...).
  • Mathijs Segers
    Mathijs Segers almost 9 years
    I know this is old, any change you also did this in swift? Since it has no setBounds func.
  • Mathijs Segers
    Mathijs Segers almost 9 years
    Nvm got it, using get & set and refer to super.bounds inside those get & set works fine.
  • Hundley
    Hundley almost 9 years
    @MathijsSegers Can you elaborate on how you got this to work?
  • Mathijs Segers
    Mathijs Segers almost 9 years
    I must say It didn't exactly "work", because I still have all the spacing but I'll post an answer with howto implement on swift. People can shoot at it there I guess.
  • Mathijs Segers
    Mathijs Segers almost 9 years
    for the record, It doesn't change the height it would be nice if someone resolved this. to let it know on this topic.
  • BallpointBen
    BallpointBen over 7 years
    For anyone who doesn't see... it's label1 vs label2 after the = signs in the second lines