Vertically align text within a UILabel (Note : Using AutoLayout)
Solution 1
Edit
In my original answer I was using the paragraph style of the label. Turns out that for multi-line labels this actually prevents the label from being multi-line. As a result I removed it from the calculation. See more about this in Github
For those of you more comfortable with using Open Source definitely look at TTTAttributedLabel where you can set the label's text alignment to TTTAttributedLabelVerticalAlignmentTop
The trick is to subclass UILabel
and override drawTextInRect
. Then enforce that the text is drawn at the origin of the label's bounds.
Here's a naive implementation that you can use right now:
Swift
@IBDesignable class TopAlignedLabel: UILabel {
override func drawTextInRect(rect: CGRect) {
if let stringText = text {
let stringTextAsNSString = stringText as NSString
var labelStringSize = stringTextAsNSString.boundingRectWithSize(CGSizeMake(CGRectGetWidth(self.frame), CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: font],
context: nil).size
super.drawTextInRect(CGRectMake(0, 0, CGRectGetWidth(self.frame), ceil(labelStringSize.height)))
} else {
super.drawTextInRect(rect)
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
layer.borderWidth = 1
layer.borderColor = UIColor.blackColor().CGColor
}
}
Swift 3
@IBDesignable class TopAlignedLabel: 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)
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
layer.borderWidth = 1
layer.borderColor = UIColor.black.cgColor
}
}
Objective-C
IB_DESIGNABLE
@interface TopAlignedLabel : UILabel
@end
@implementation TopAlignedLabel
- (void)drawTextInRect:(CGRect)rect {
if (self.text) {
CGSize labelStringSize = [self.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.frame), CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName:self.font}
context:nil].size;
[super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)),ceilf(labelStringSize.height))];
} else {
[super drawTextInRect:rect];
}
}
- (void)prepareForInterfaceBuilder {
[super prepareForInterfaceBuilder];
self.layer.borderWidth = 1;
self.layer.borderColor = [UIColor blackColor].CGColor;
}
@end
Since I used IBDesignable you can add this label to a storyboard and watch it go, this is what it looks like for me
Solution 2
If you're not restricted by having UILabel of fixed size, instead of aligning the text within a UILabel, simply use ≥ constraint on the given label to change the size of it.
It's the most elegant solution using Auto Layout. Don't forget to set numberOfLines to zero though.
Solution 3
You can use UITextView instead of UILabel:
Uncheck "Scrolling enabled"
Uncheck "Editable"
Uncheck "Selectable"
Set background color to ClearColor
Solution 4
I had the same problem, and this is how I solved it. I just edited the Baseline under Attribute Inspector for the Label. Set it to "Align Centers".
Solution 5
Instead, I changed the Bottom Space Constant to priority @250 and solved my problem. And my label has height constant with <= constant
MELWIN
Updated on November 26, 2020Comments
-
MELWIN over 3 years
I am Copying the same Question asked Before Question. I have tried the solutions given and was not able to solve it since sizetofit was not effective when I use Autolayout.
The expected display is like below.
-
MELWIN about 9 yearsYes, the label height is connected to another view's top placed below the label. I understood your solution, but that makes my tablecellview more complicated, it already have a lot of constrains connecting each other. Will you please suggest me another solution like sizetofit ?
-
Wain about 9 yearsSize to fit reduces the height of the label, so it's more complicated because you have specified different requirements...
-
MELWIN about 9 yearsI didnt get the option [label setContentVerticalAlignmen...] for UILabel. It is for Button only right ?
-
MELWIN almost 9 yearsCan u help me to solve the same case when it comes in case of TTTAttributedLabel.
-
Daniel Galasko almost 9 years@MELWIN TTTAttributedLabel has a verticalAlignment property that you can set to .Top
-
MELWIN almost 9 years@daneil Thank u. Worked fine
-
MK Yung almost 9 years@DanielGalasko if the text is longer than the width of the label it turns into ellipsis rather than going next line. I have already set
Lines: 0
in the XCode but it does not work. How to fix it? -
Daniel Galasko almost 9 years@MKYung set numberOfLines to 0 and make sure you set the preferredMaxLayoutWidth to the width of the labels frame
-
daspianist almost 9 years@DanielGalasko Thank you for the solution. I seem to have the same problem as MK (I also ensured that lines were set to 0, and
preferredMaxLayoutWidth = YES
. When testing using static text that's 2 lines, this solution works. However, when I have a long text, the "..." ellipses show up. Thanks for looking into it. -
Daniel Galasko almost 9 years@daspianist preferredMaxLayoutWidth is not a Boolean, perhaps that's your error?
-
daspianist almost 9 years@DanielGalasko ah you're right, and thanks for your reply! I am using Storyboard (the
UILabel
appear inside aUITableViewCell
), and changed the max width to the same width that the label appears inside my cell. However, the ellipses persist... -
Daniel Galasko almost 9 yearsDarn, maybe setup a github repo so we can get to the bottom of this :) @daspianist
-
daspianist almost 9 yearsThank you @DanielGalasko! Basic repo is here: github.com/zallanx/TopAlignedLabelTest
-
Daniel Galasko almost 9 years@daspianist solved the problem and updated the SO post as well as submitted a PR on your Github. Thanks so much for the effort!
-
daspianist almost 9 years@DanielGalasko Thank you for the effort and your excellent answer! Appreciate all your time.
-
MizAkita about 8 yearsthis works fine for me... didn't have to override a class or anything a uitextbox did it... thanks a lot for this!
-
Mark Barrasso about 8 yearsSimple solution without using custom IBDesignables. Worked perfectly for me using in iOS 9.2 / Xcode 7.2.1
-
Mark Barrasso about 8 yearsAlso, in order for the text within the UITextView to align perfectly with another UILabel, I used this line of code:
self.textView.textContainer.lineFragmentPadding = 0;
-
CharlesA about 8 years@DanielGalasko I found that using this code with text that was too big to fit the label's frame led to an offset appearing at the top. Fixed with
CGFloat height = MIN(labelStringSize.height, CGRectGetHeight(self.frame)); [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)), height)];
-
siva krishna over 7 yearsIt works but I'm getting error in story board like this "Failed to render instance of TopAlignedLabel: dlopen.........................."
-
elquimista over 7 yearsAlthough this seems to be working, lineBreakMode with TailTrailing doesn't work. Looks like this behavior is overridden by subclassing. What's the fix?
-
Zack over 7 yearsYes! Best, easiest, most elegant solution here.
-
Dale about 7 yearsSimilarly you can set height <= something
-
Glenn Sønderskov almost 7 yearsEasy solution! Worked for me too
-
Ruiz about 6 yearsThis only works if the containing view doesn't rely on the label to determine it's width and/or height.
-
Darkwonder about 6 yearsBest solution so far. Works on iOS 11.2 / XCode 9.2
-
Luis Pena almost 6 yearsI'm wondering why would this work, what's the science behind?
-
David Pettigrew over 5 yearsAs others have stated, it doesn't work if the text is truncated. e.g. Using
numberOfLines = 2
withlineBreakMode = .byTruncatingTail
and the text doesn't fit. There is extra padding above the text. -
NSGangster over 4 years@LuisPena By default Vertical Content Hugging priority on views set in storyboard is 251, when layout engine sees bottom constraint only has 250 priority but priority to hug content is higher, it chooses to hug content. By default constraints are set to 1000, which is why it only works by setting priority lower for engine to "break" constraint, I prefer having the >= constraint as in Nikolai's answer