Autolayout adding extra padding on top and bottom of UILabel iPhone 6/6+ when preferredMaxLayoutWidth is set
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)
}
}
}
Related videos on Youtube
newDeveloper
Updated on September 16, 2022Comments
-
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:
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).
With preferredMaxLayoutWidth. Now the UILabel shows the entire content but has a padding on top and bottom.
Edit2: Sample Project: https://www.dropbox.com/s/sdahx9njnsxiv98/MultiLineUILabel.zip?dl=1
-
Michael Gaylord over 9 yearsI 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 over 9 yearsI 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 about 9 yearsMake 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 over 7 yearsLink is dead, please update your answer
-
-
newDeveloper over 9 yearsSo 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 over 9 yearsYes, 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 over 9 yearsOk. 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 over 9 yearsIf 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 over 9 yearsSure, dropbox.com/s/sdahx9njnsxiv98/MultiLineUILabel.zip?dl=1 I turned off the preferredMaxLayoutWidth. Turn it on to see multi-line label.
-
newDeveloper over 9 yearsThat'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 almost 9 yearsI know this is old, any change you also did this in swift? Since it has no setBounds func.
-
Mathijs Segers almost 9 yearsNvm got it, using get & set and refer to super.bounds inside those get & set works fine.
-
Hundley almost 9 years@MathijsSegers Can you elaborate on how you got this to work?
-
Mathijs Segers almost 9 yearsI 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 almost 9 yearsfor the record, It doesn't change the height it would be nice if someone resolved this. to let it know on this topic.
-
BallpointBen over 7 yearsFor anyone who doesn't see... it's
label1
vslabel2
after the=
signs in the second lines