Related
I am drawing label using drawRect and the code looks like something below.
if (productName && productName.length > 0) {
UILabel *productNameLabel = [[UILabel alloc]init];
productNameLabel.numberOfLines = 2;
productNameLabel.attributedText = [self shadowedTextWithString:productName fontName:#"ProximaNovaA-Light" fontSize:productNameLabelFontSize isOfferType:NO];
[productNameLabel sizeToFit];
//drawing the UILabel
[productNameLabel drawTextInRect:CGRectMake(25, labelYPosition, productNameLabel.frame.size.width, productNameLabel.frame.size.height)];
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 25, labelYPosition);
[productNameLabel.layer renderInContext:UIGraphicsGetCurrentContext()];
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -25, -labelYPosition);
labelYPosition += productNameLabel.frame.origin.y + productNameLabel.frame.size.height+20;
}
However, the productNameLabel.numberOfLiness = 2 doesn't seem to work at all... If the string has length that exceeds the width of the screen, the text is truncated and the UILabel stays one liner.
Anyone knows how do i do it, so that if the length of the string exceeds the width of screen, the exceeded words will go to the second line?
Thanks!
updated code, still doesn't work !
if (productName && productName.length > 0) {
UILabel *productNameLabel = [[UILabel alloc]init];
productNameLabel.lineBreakMode = YES;
productNameLabel.numberOfLines = 0;
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineBreakMode = NSLineBreakByTruncatingTail;
NSMutableAttributedString *productNameAttributedString = [self shadowedTextWithString:productName fontName:#"ProximaNovaA-Light" fontSize:productNameLabelFontSize isOfferType:NO];
[productNameAttributedString addAttribute:NSParagraphStyleAttributeName
value:style
range:NSMakeRange(0, productNameAttributedString.length)];
productNameLabel.attributedText = productNameAttributedString;
CGSize constrainedSize = CGSizeMake(paramImageView.image.size.width -50 , 9999);
CGRect requiredHeight = [productNameLabel.attributedText boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (requiredHeight.size.width > productNameLabel.frame.size.width) {
requiredHeight = CGRectMake(25,labelYPosition, productNameLabel.frame.size.width, requiredHeight.size.height);
}
CGRect newFrame = productNameLabel.frame;
newFrame.size.height = requiredHeight.size.height;
productNameLabel.frame = newFrame;
productNameLabel.backgroundColor = [UIColor redColor];
[productNameLabel drawTextInRect:CGRectMake(25, labelYPosition, paramImageView.image.size.width-50, requiredHeight.size.height)];
//[productNameLabel drawTextInRect:CGRectMake(25, labelYPosition, 30, productNameLabel.frame.size.height)];
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 25, labelYPosition);
[productNameLabel.layer renderInContext:UIGraphicsGetCurrentContext()];
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), -25, -labelYPosition);
labelYPosition += productNameLabel.frame.origin.y + productNameLabel.frame.size.height+20;
}
Objective-c
CGSize sizeToFit = [title sizeWithFont:productNameLabel.font constrainedToSize:productNameLabel.frame.size lineBreakMode:productNameLabel.lineBreakMode];
Swift 2.2
var sizeToFit = title.sizeWithFont(productNameLabel.font, constrainedToSize: productNameLabel.frame.size, lineBreakMode: productNameLabel.lineBreakMode)
Swift3.0
var sizeToFit: CGSize = title.size(with: productNameLabel.font, constrainedTo: productNameLabel.frame.size, lineBreakMode: productNameLabel.lineBreakMode)
CGSize constrainedSize = CGSizeMake(self.resizableLable.frame.size.width , 9999);
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"HelveticaNeue" size:11.0], NSFontAttributeName,
nil];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"textToShow" attributes:attributesDictionary];
CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (requiredHeight.size.width > self.resizableLable.frame.size.width) {
requiredHeight = CGRectMake(0,0, self.resizableLable.frame.size.width, requiredHeight.size.height);
}
CGRect newFrame = self.resizableLable.frame;
newFrame.size.height = requiredHeight.size.height;
self.resizableLable.frame = newFrame;
Implement this method to find width of string pass font as an argument you want to give to string and this method will return width of string.
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSFontAttributeName: font]
let size = self.size(attributes: fontAttributes)
return size.width
}
Set this width as a width of the UILabel
Swift-3:
Returns the height of label with padding depending on the text.
func heightForView(text: String, font: UIFont, width: CGFloat) -> CGFloat {
let label = UILabel(frame: CGRect.init(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height + labelHeightPadding ///extra padding; if needed be.
}
I have a UILabel with space for two lines of text. Sometimes, when the text is too short, this text is displayed in the vertical center of the label.
How do I vertically align the text to always be at the top of the UILabel?
There's no way to set the vertical-align on a UILabel, but you can get the same effect by changing the label's frame. I've made my labels orange so you can see clearly what's happening.
Here's the quick and easy way to do this:
[myLabel sizeToFit];
If you have a label with longer text that will make more than one line, set numberOfLines to 0 (zero here means an unlimited number of lines).
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
Longer Version
I'll make my label in code so that you can see what's going on. You can set up most of this in Interface Builder too. My setup is a View-Based App with a background image I made in Photoshop to show margins (20 points). The label is an attractive orange color so you can see what's going on with the dimensions.
- (void)viewDidLoad
{
[super viewDidLoad];
// 20 point top and left margin. Sized to leave 20 pt at right.
CGRect labelFrame = CGRectMake(20, 20, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setBackgroundColor:[UIColor orangeColor]];
NSString *labelText = #"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
[myLabel setText:labelText];
// Tell the label to use an unlimited number of lines
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
[self.view addSubview:myLabel];
}
Some limitations of using sizeToFit come into play with center- or right-aligned text. Here's what happens:
// myLabel.textAlignment = NSTextAlignmentRight;
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
The label is still sized with a fixed top-left corner. You can save the original label's width in a variable and set it after sizeToFit, or give it a fixed width to counter these problems:
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
CGRect myFrame = myLabel.frame;
// Resize the frame's width to 280 (320 - margins)
// width could also be myOriginalLabelFrame.size.width
myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
myLabel.frame = myFrame;
Note that sizeToFit will respect your initial label's minimum width. If you start with a label 100 wide and call sizeToFit on it, it will give you back a (possibly very tall) label with 100 (or a little less) width. You might want to set your label to the minimum width you want before resizing.
Some other things to note:
Whether lineBreakMode is respected depends on how it's set. NSLineBreakByTruncatingTail (the default) is ignored after sizeToFit, as are the other two truncation modes (head and middle). NSLineBreakByClipping is also ignored. NSLineBreakByCharWrapping works as usual. The frame width is still narrowed to fit to the rightmost letter.
Mark Amery gave a fix for NIBs and Storyboards using Auto Layout in the comments:
If your label is included in a nib or storyboard as a subview of the view of a ViewController that uses autolayout, then putting your sizeToFit call into viewDidLoad won't work, because autolayout sizes and positions the subviews after viewDidLoad is called and will immediately undo the effects of your sizeToFit call. However, calling sizeToFit from within viewDidLayoutSubviews will work.
My Original Answer (for posterity/reference):
This uses the NSString method sizeWithFont:constrainedToSize:lineBreakMode: to calculate the frame height needed to fit a string, then sets the origin and width.
Resize the frame for the label using the text you want to insert. That way you can accommodate any number of lines.
CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = #"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:#"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont
constrainedToSize:maximumSize
lineBreakMode:self.dateLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);
self.dateLabel.frame = dateFrame;
Set the new text:
myLabel.text = #"Some Text"
Set the maximum number of lines to 0 (automatic):
myLabel.numberOfLines = 0
Set the frame of the label to the maximum size:
myLabel.frame = CGRectMake(20,20,200,800)
Call sizeToFit to reduce the frame size so the contents just fit:
[myLabel sizeToFit]
The labels frame is now just high and wide enough to fit your text. The top left should be unchanged. I have tested this only with the top left-aligned text. For other alignments, you might have to modify the frame afterward.
Also, my label has word wrapping enabled.
Refering to the extension solution:
for(int i=1; i< newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n"];
should be replaced by
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
Additional space is needed in every added newline, because iPhone UILabels' trailing carriage returns seems to be ignored :(
Similarly, alignBottom should be updated too with a #" \n#%" in place of "\n#%" (for cycle initialization must be replaced by "for(int i=0..." too).
The following extension works for me:
// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
// -- file: UILabel+VerticalAlign.m
#implementation UILabel (VerticalAlign)
- (void)alignTop {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
}
- (void)alignBottom {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
#end
Then call [yourLabel alignTop]; or [yourLabel alignBottom]; after each yourLabel text assignment.
Just in case it's of any help to anyone, I had the same problem but was able to solve the issue simply by switching from using UILabel to using UITextView. I appreciate this isn't for everyone because the functionality is a bit different.
If you do switch to using UITextView, you can turn off all the Scroll View properties as well as User Interaction Enabled... This will force it to act more like a label.
No muss, no fuss
#interface MFTopAlignedLabel : UILabel
#end
#implementation MFTopAlignedLabel
- (void)drawTextInRect:(CGRect) rect
{
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text attributes:#{NSFontAttributeName:self.font}];
rect.size.height = [attributedText boundingRectWithSize:rect.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
if (self.numberOfLines != 0) {
rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
}
[super drawTextInRect:rect];
}
#end
No muss, no Objective-c, no fuss but Swift 3:
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Swift 4.2
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Easiest approach using Storyboard:
Embed Label in a StackView and set the following two attributes of StackView in the Attribute Inspector:
1- Axis to Horizontal,
2- Alignment to Top
Like the answer above, but it wasn't quite right, or easy to slap into code so I cleaned it up a bit. Add this extension either to it's own .h and .m file or just paste right above the implementation you intend to use it:
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
#implementation UILabel (VerticalAlign)
- (void)alignTop
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<= newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#" \n"];
}
}
- (void)alignBottom
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i< newLinesToPad; i++)
{
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
}
#end
And then to use, put your text into the label, and then call the appropriate method to align it:
[myLabel alignTop];
or
[myLabel alignBottom];
An even quicker (and dirtier) way to accomplish this is by setting the UILabel's line break mode to "Clip" and adding a fixed amount of newlines.
myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];
This solution won't work for everyone -- in particular, if you still want to show "..." at the end of your string if it exceeds the number of lines you're showing, you'll need to use one of the longer bits of code -- but for a lot of cases this'll get you what you need.
Instead of UILabel you may use UITextField which has vertical alignment option:
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
I've struggled with this one for a long time and I wanted to share my solution.
This will give you a UILabel that will autoshrink text down to 0.5 scales and vertically center the text. These options are also available in Storyboard/IB.
[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
Create a new class
LabelTopAlign
.h file
#import <UIKit/UIKit.h>
#interface KwLabelTopAlign : UILabel {
}
#end
.m file
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect {
int lineHeight = [#"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
if(rect.size.height >= lineHeight) {
int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
int yMax = textHeight;
if (self.numberOfLines > 0) {
yMax = MIN(lineHeight*self.numberOfLines, yMax);
}
[super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
}
}
#end
Edit
Here's a simpler implementation that does the same:
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect
{
CGFloat height = [self.text sizeWithFont:self.font
constrainedToSize:rect.size
lineBreakMode:self.lineBreakMode].height;
if (self.numberOfLines != 0) {
height = MIN(height, self.font.lineHeight * self.numberOfLines);
}
rect.size.height = MIN(rect.size.height, height);
[super drawTextInRect:rect];
}
#end
In Interface Builder
Set UILabel to size of biggest possible Text
Set Lines to '0' in Attributes Inspector
In your code
Set the text of the label
Call sizeToFit on your label
Code Snippet:
self.myLabel.text = #"Short Title";
[self.myLabel sizeToFit];
For Adaptive UI(iOS8 or after) , Vertical Alignment of UILabel is to be set from StoryBoard by Changing the properties
noOfLines=0` and
Constraints
Adjusting UILabel LefMargin, RightMargin and Top Margin Constraints.
Change Content Compression Resistance Priority For Vertical=1000` So that Vertical>Horizontal .
Edited:
noOfLines=0
and the following constraints are enough to achieve the desired results.
Create a subclass of UILabel. Works like a charm:
// TopLeftLabel.h
#import <Foundation/Foundation.h>
#interface TopLeftLabel : UILabel
{
}
#end
// TopLeftLabel.m
#import "TopLeftLabel.h"
#implementation TopLeftLabel
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
textRect.origin.y = bounds.origin.y;
return textRect;
}
-(void)drawTextInRect:(CGRect)requestedRect
{
CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
}
#end
As discussed here.
What I did in my app was to set the UILabel's line property to 0 as well as to create a bottom constraint of the UILabel and make sure it is being set to >= 0 as shown in the image below.
Use textRect(forBounds:limitedToNumberOfLines:).
class TopAlignedLabel: UILabel {
override func drawText(in rect: CGRect) {
let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
super.drawText(in: textRect)
}
}
I wrote a util function to achieve this purpose. You can take a look:
// adjust the height of a multi-line label to make it align vertical with top
+ (void) alignLabelWithTop:(UILabel *)label {
CGSize maxSize = CGSizeMake(label.frame.size.width, 999);
label.adjustsFontSizeToFitWidth = NO;
// get actual height
CGSize actualSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
CGRect rect = label.frame;
rect.size.height = actualSize.height;
label.frame = rect;
}
.How to use? (If lblHello is created by Interface builder, so I skip some UILabel attributes detail)
lblHello.text = #"Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop:lblHello];
I also wrote it on my blog as an article:
http://fstoke.me/blog/?p=2819
I took a while to read the code, as well as the code in the introduced page, and found that they all try to modify the frame size of label, so that the default center vertical alignment would not appear.
However, in some cases we do want the label to occupy all those spaces, even if the label does have so much text (e.g. multiple rows with equal height).
Here, I used an alternative way to solve it, by simply pad newlines to the end of label (pls note that I actually inherited the UILabel, but it is not necessary):
CGSize fontSize = [self.text sizeWithFont:self.font];
finalHeight = fontSize.height * self.numberOfLines;
finalWidth = size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i = 0; i < newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#"\n "];
}
I took the suggestions here and created a view which can wrap a UILabel and will size it and set the number of lines so that it is top aligned. Simply put a UILabel as a subview:
#interface TopAlignedLabelContainer : UIView
{
}
#end
#implementation TopAlignedLabelContainer
- (void)layoutSubviews
{
CGRect bounds = self.bounds;
for (UILabel *label in [self subviews])
{
if ([label isKindOfClass:[UILabel class]])
{
CGSize fontSize = [label.text sizeWithFont:label.font];
CGSize textSize = [label.text sizeWithFont:label.font
constrainedToSize:bounds.size
lineBreakMode:label.lineBreakMode];
label.numberOfLines = textSize.height / fontSize.height;
label.frame = CGRectMake(0, 0, textSize.width,
fontSize.height * label.numberOfLines);
}
}
}
#end
You can use TTTAttributedLabel, it supports vertical alignment.
#property (nonatomic) TTTAttributedLabel* label;
<...>
//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
I've found the answers on this question are now a bit out-of-date, so adding this for the auto layout fans out there.
Auto layout makes this issue pretty trivial. Assuming we're adding the label to UIView *view, the following code will accomplish this:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:#"Some text here"];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:label];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[label]|" options:0 metrics:nil views:#{#"label": label}]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label]" options:0 metrics:nil views:#{#"label": label}]];
The label's height will be calculated automatically (using it's intrinsicContentSize) and the label will be positioned edge-to-edge horizontally, at the top of the view.
I've used a lot of the methods above, and just want to add a quick-and-dirty approach I've used:
myLabel.text = [NSString stringWithFormat:#"%#\n\n\n\n\n\n\n\n\n",#"My label text string"];
Make sure the number of newlines in the string will cause any text to fill the available vertical space, and set the UILabel to truncate any overflowing text.
Because sometimes good enough is good enough.
I wanted to have a label which was able to have multi-lines, a minimum font size, and centred both horizontally and vertically in it's parent view. I added my label programmatically to my view:
- (void) customInit {
// Setup label
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.label.numberOfLines = 0;
self.label.lineBreakMode = UILineBreakModeWordWrap;
self.label.textAlignment = UITextAlignmentCenter;
// Add the label as a subview
self.autoresizesSubviews = YES;
[self addSubview:self.label];
}
And then when I wanted to change the text of my label...
- (void) updateDisplay:(NSString *)text {
if (![text isEqualToString:self.label.text]) {
// Calculate the font size to use (save to label's font)
CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
}
// In cases where the frame is still too large (when we're exceeding minimum font size),
// use the views size
if (textSize.height > self.frame.size.height) {
textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
}
// Draw
self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
self.label.text = text;
}
[self setNeedsDisplay];
}
Hope that helps someone!
FXLabel (on github) does this out of the box by setting label.contentMode to UIViewContentModeTop. This component is not made by me, but it is a component I use frequently and has tons of features, and seems to work well.
for anyone reading this because the text inside your label is not vertically centered, keep in mind that some font types are not designed equally. for example, if you create a label with zapfino size 16, you will see the text is not perfectly centered vertically.
however, working with helvetica will vertically center your text.
Subclass UILabel and constrain the drawing rectangle, like this:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
[super drawTextInRect:rect];
}
I tried the solution involving newline padding and ran into incorrect behavior in some cases. In my experience, it's easier to constrain the drawing rect as above than mess with numberOfLines.
P.S. You can imagine easily supporting UIViewContentMode this way:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
if (self.contentMode == UIViewContentModeTop) {
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
else if (self.contentMode == UIViewContentModeBottom) {
rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
[super drawTextInRect:rect];
}
If you are using autolayout, set the vertical contentHuggingPriority to 1000, either in code or IB. In IB you may then have to remove a height constraint by setting it's priority to 1 and then deleting it.
As long as you are not doing any complex task, you can use UITextView instead of UILabels.
Disable the scroll.
If you want the text to be displayed completely just user sizeToFit and sizeThatFits: methods
In swift,
let myLabel : UILabel!
To make your text of your Label to fit to screen and it's on the top
myLabel.sizeToFit()
To make your font of label to fit to the width of screen or specific width size.
myLabel.adjustsFontSizeToFitWidth = YES
and some textAlignment for label :
myLabel.textAlignment = .center
myLabel.textAlignment = .left
myLabel.textAlignment = .right
myLabel.textAlignment = .Natural
myLabel.textAlignment = .Justified
This is an old solution, use the autolayout on iOS >= 6
My solution:
Split lines by myself (ignoring label wrap settings)
Draw lines by myself (ignoring label alignment)
#interface UITopAlignedLabel : UILabel
#end
#implementation UITopAlignedLabel
#pragma mark Instance methods
- (NSArray*)splitTextToLines:(NSUInteger)maxLines {
float width = self.frame.size.width;
NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSMutableArray* lines = [NSMutableArray array];
NSMutableString* buffer = [NSMutableString string];
NSMutableString* currentLine = [NSMutableString string];
for (NSString* word in words) {
if ([buffer length] > 0) {
[buffer appendString:#" "];
}
[buffer appendString:word];
if (maxLines > 0 && [lines count] == maxLines - 1) {
[currentLine setString:buffer];
continue;
}
float bufferWidth = [buffer sizeWithFont:self.font].width;
if (bufferWidth < width) {
[currentLine setString:buffer];
}
else {
[lines addObject:[NSString stringWithString:currentLine]];
[buffer setString:word];
[currentLine setString:buffer];
}
}
if ([currentLine length] > 0) {
[lines addObject:[NSString stringWithString:currentLine]];
}
return lines;
}
- (void)drawRect:(CGRect)rect {
if ([self.text length] == 0) {
return;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.textColor.CGColor);
CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);
NSArray* lines = [self splitTextToLines:self.numberOfLines];
NSUInteger numLines = [lines count];
CGSize size = self.frame.size;
CGPoint origin = CGPointMake(0.0f, 0.0f);
for (NSUInteger i = 0; i < numLines; i++) {
NSString* line = [lines objectAtIndex:i];
if (i == numLines - 1) {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];
}
else {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
}
origin.y += self.font.lineHeight;
if (origin.y >= size.height) {
return;
}
}
}
#end
I am dynamically creating UILabel in my app to display a list of directions for a recipe. The labels populate correctly displaying all the items one after another.
The problem is when the text goes on next line of the label, it is overlapped with the next label.
I have set numberOfLines to 0 and also set lineBreakMode to NSLineBreakByWordWrapping. This helps the label to display text on multiple lines.
I have tried to adjust the height of the label, as you will see in the code, but it doesn't work.
How do I prevent the overlapping of labels due to multiline text in label?
Here is the code for populating the labels with multilines:
//add all the directions to the uiview
for (int i = 0; i < self.recipe.directions.count; i++)
{
UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake(0,(i+1)*25,280,25)];
label.lineBreakMode = NSLineBreakByWordWrapping; //multiple lines in a label
label.numberOfLines = 0;
label.text =[NSString stringWithFormat: #"%#", self.recipe.directions[i]];
[label sizeToFit]; // resize the width and height to fit the text
NSLog(#"Actual height is: %f", label.frame.size.height); // Use this for spacing any further elements
CGSize expectedLabelSize = [label.text sizeWithFont:label.font
constrainedToSize:label.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
//adjust the label the the new height.
CGRect newFrame = label.frame;
newFrame.size.height = expectedLabelSize.height;
label.frame = newFrame;
[self.directionsView addSubview:label];
}
Instead of initialising your label with an y position of (i+1)*25, you should store your last label's bottom position
CGFloat lastLabelBottomCoordinate = 25;
CGFloat spaceBetweenLines = 10;
for (int i = 0; i < 10; i++)
{
UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake(0, lastLabelBottomCoordinate + spaceBetweenLines,280,25)];
label.lineBreakMode = NSLineBreakByWordWrapping; //multiple lines in a label
label.numberOfLines = 0;
label.text =[NSString stringWithFormat: #"This is a very long text to see if the text have more than 2 lines"];
[label sizeToFit]; // resize the width and height to fit the text
NSLog(#"Actual height is: %f", label.frame.size.height); // Use this for spacing any further elements
CGSize expectedLabelSize = [label.text boundingRectWithSize:CGSizeMake(label.frame.size.width, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName : label.font}
context:nil].size;
//adjust the label the the new height.
CGRect newFrame = label.frame;
newFrame.size.height = expectedLabelSize.height;
label.frame = newFrame;
lastLabelBottomCoordinate = label.frame.origin.y + label.frame.size.height;
[self.view addSubview:label];
}
This is how it looks:
I have a UILabel, his text size has the property
title.adjustsFontSizeToFitWidth = YES;
that prevents me from using standard methods to resize the UILabel. I read on another post here that I'm supposed to use the function
sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode
from this answer: How to figure out the font size of a UILabel when -adjustsFontSizeToFitWidth is set to YES?
Now, i can't figure out how to make it work.. this is the actual code
UIFont *font = [UIFont fontWithName:#"Marker Felt" size:200];
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, width, 20.0)];
title.text = #"this is a long title that should be resized";
title.font = font;
title.numberOfLines = 1;
title.adjustsFontSizeToFitWidth = YES;
CGFloat pointSize = 0.0;
CGSize size = [title.text sizeWithFont:font
minFontSize:title.minimumFontSize
actualFontSize:&pointSize
forWidth:width
lineBreakMode:title.lineBreakMode];
title.frame = CGRectMake(title.frame.origin.x,
title.frame.origin.y,
size.width,
size.height);
The UILabel get resized wrongly, as if the font size it's still 200..
Any clues? Thanks!
I have some code you could use on my github, check it out, it's a category for UILabel, you need to set the frame width and when you can resizeToFit on the UILabel, it adjusts the height to fit the content, and returns the y position of the end of the label so you can adjust any content appearing after it.
https://gist.github.com/1005520
I'd suggest filing this as a bug.
The size returned by -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: has the correct width, but not the height does not account for the actual font size.
It seems likely that UILabel also has this bug. Changing the size of a label to match the height of the text in a font of the size returned by -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: will incorrectly vertically position the text within the label.
A work-around is to calculate the correct height, and change the font on the label to with the actual font size:
CGFloat pointSize = 0.0f;
CGRect frame = title.frame;
frame.size = [title.text sizeWithFont:font
minFontSize:title.minimumFontSize
actualFontSize:&pointSize
forWidth:width
lineBreakMode:title.lineBreakMode];
UIFont *actualFont = [UIFont fontWithName:#"Marker Felt" size:pointSize];
CGSize sizeWithCorrectHeight = [title.text sizeWithFont:actualFont];
frame.size.height = sizeWithCorrectHeight.height;
title.frame = frame;
title.font = actualFont;
Try to create font with smaller fontSize. For example:
UIFont *font = [UIFont fontWithName:#"Marker Felt" size:20];
but to actualFontSize pass link to CGFloat == 200.
UPDATED:
try this then:
UIFont *font = [UIFont fontWithName:#"Marker Felt" size:20];
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, width, 20.0)];
title.text = #"this is a long title that should be resized";
title.font = font;
title.numberOfLines = 1;
title.adjustsFontSizeToFitWidth = YES;
CGFloat pointSize = 0.0;
CGSize size = [title.text sizeWithFont:font minFontSize:title.minimumFontSize actualFontSize:&pointSize forWidth:width lineBreakMode:title.lineBreakMode];
title.frame = CGRectMake(title.frame.origin.x, title.frame.origin.y, size.width, size.height);
font = [UIFont fontWithName:#"Marker Felt" size:200];
title.font = font;
UILabel * label = [[UILabel alloc] init];
[label setNumberOfLines:0];
label.text=[detailDict valueForKey:#"method"];
[label setFont:[UIFont fontWithName:#"Georgia" size:16.0]];
CGSize labelsize=[label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(250, 1000.0) lineBreakMode:UILineBreakModeWordWrap];
int y=0;
label.frame=CGRectMake(38, y, 245, labelsize.height);
[label setBackgroundColor:[UIColor clearColor]];
[label setTextColor:[UIColor whiteColor]];
scrollView.showsVerticalScrollIndicator = NO;
y+=labelsize.height;
[scrollView setContentSize:CGSizeMake(200,y+50)];
[scrollView addSubview:label];
[label release];
I think you should use this code and i am using the label on scrollview for a big text you can also do so
Have you tried intrinsicContentSize?
myLable.numberOfLines = 0
myLable.frame = CGRect(x: 10, y: 10, width: 300, height: lblSuperscript.intrinsicContentSize().height)
Is there a way to have multiple lines of text in UILabel like in the UITextView or should I use the second one instead?
Set the line break mode to word-wrapping and the number of lines to 0:
// Swift
textLabel.lineBreakMode = .byWordWrapping
textLabel.numberOfLines = 0
// Objective-C
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.numberOfLines = 0;
// C# (Xamarin.iOS)
textLabel.LineBreakMode = UILineBreakMode.WordWrap;
textLabel.Lines = 0;
Restored old answer (for reference and devs willing to support iOS below 6.0):
textLabel.lineBreakMode = UILineBreakModeWordWrap;
textLabel.numberOfLines = 0;
On the side: both enum values yield to 0 anyway.
In IB, set number of lines to 0 (allows unlimited lines)
When typing within the text field using IB, use "alt-return" to insert a return and go to the next line (or you can copy in text already separated out by lines).
The best solution I have found (to an otherwise frustrating problem that should have been solved in the framework) is similar to vaychick's.
Just set number of lines to 0 in either IB or code
myLabel.numberOfLines = 0;
This will display the lines needed but will reposition the label so its centered horizontally (so that a 1 line and 3 line label are aligned in their horizontal position). To fix that add:
CGRect currentFrame = myLabel.frame;
CGSize max = CGSizeMake(myLabel.frame.size.width, 500);
CGSize expected = [myString sizeWithFont:myLabel.font constrainedToSize:max lineBreakMode:myLabel.lineBreakMode];
currentFrame.size.height = expected.height;
myLabel.frame = currentFrame;
Use this to have multiple lines of text in UILabel:
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.numberOfLines = 0;
Swift:
textLabel.lineBreakMode = .byWordWrapping
textLabel.numberOfLines = 0
myUILabel.numberOfLines = 0;
myUILabel.text = #"your long string here";
[myUILabel sizeToFit];
If you have to use the:
myLabel.numberOfLines = 0;
property you can also use a standard line break ("\n"), in code, to force a new line.
You can use \r to go to next line while filling up the UILabel using NSString.
UILabel * label;
label.text = [NSString stringWithFormat:#"%# \r %#",#"first line",#"seconcd line"];
lets try this
textLabel.lineBreakMode = NSLineBreakModeWordWrap; // UILineBreakModeWordWrap deprecated
textLabel.numberOfLines = 0;
textLabel.lineBreakMode = UILineBreakModeWordWrap;
textLabel.numberOfLines = 0;
The solution above does't work in my case. I'm doing like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ...
CGSize size = [str sizeWithFont:[UIFont fontWithName:#"Georgia-Bold" size:18.0] constrainedToSize:CGSizeMake(240.0, 480.0) lineBreakMode:UILineBreakModeWordWrap];
return size.height + 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// ...
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Georgia-Bold" size:18.0];
}
// ...
UILabel *textLabel = [cell textLabel];
CGSize size = [text sizeWithFont:[UIFont fontWithName:#"Georgia-Bold" size:18.0]
constrainedToSize:CGSizeMake(240.0, 480.0)
lineBreakMode:UILineBreakModeWordWrap];
cell.textLabel.frame = CGRectMake(0, 0, size.width + 20, size.height + 20);
//...
}
Swift 3
Set number of lines zero for dynamic text information, it will be useful for varying text.
var label = UILabel()
let stringValue = "A label\nwith\nmultiline text."
label.text = stringValue
label.numberOfLines = 0
label.lineBreakMode = .byTruncatingTail // or .byWrappingWord
label.minimumScaleFactor = 0.8 . // It is not required but nice to have a minimum scale factor to fit text into label frame
Use story borad : select the label to set number of lines to zero......Or Refer this
Try using this:
lblName.numberOfLines = 0;
[lblName sizeToFit];
UILabel *helpLabel = [[UILabel alloc] init];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:label];
helpLabel.attributedText = attrString;
// helpLabel.text = label;
helpLabel.textAlignment = NSTextAlignmentCenter;
helpLabel.lineBreakMode = NSLineBreakByWordWrapping;
helpLabel.numberOfLines = 0;
For some reasons its not working for me in iOS 6 not sure why. Tried it with and without attributed text. Any suggestions.
Method 1:
extension UILabel {//Write this extension after close brackets of your class
func lblFunction() {
numberOfLines = 0
lineBreakMode = .byWordWrapping//If you want word wraping
//OR
lineBreakMode = .byCharWrapping//If you want character wraping
}
}
Now call simply like this
myLbl.lblFunction()//Replace your label name
EX:
Import UIKit
class MyClassName: UIViewController {//For example this is your class.
override func viewDidLoad() {
super.viewDidLoad()
myLbl.lblFunction()//Replace your label name
}
}//After close of your class write this extension.
extension UILabel {//Write this extension after close brackets of your class
func lblFunction() {
numberOfLines = 0
lineBreakMode = .byWordWrapping//If you want word wraping
//OR
lineBreakMode = .byCharWrapping//If you want character wraping
}
}
Method 2:
Programmatically
yourLabel.numberOfLines = 0
yourLabel.lineBreakMode = .byWordWrapping//If you want word wraping
//OR
yourLabel.lineBreakMode = .byCharWrapping//If you want character wraping
Method 3:
Through Story board
To display multiple lines set 0(Zero), this will display more than one line in your label.
If you want to display n lines, set n.
See below screen.
If you want to set minimum font size for label Click Autoshrink and Select Minimum Font Size option
See below screens
Here set minimum font size
EX: 9 (In this image)
If your label get more text at that time your label text will be shrink upto 9
These things helped me
Change these properties of UILabel
label.numberOfLines = 0;
label.adjustsFontSizeToFitWidth = NO;
label.lineBreakMode = NSLineBreakByWordWrapping;
And while giving input String use \n to display different words in different lines.
Example :
NSString *message = #"This \n is \n a demo \n message for \n stackoverflow" ;
Swift 4:
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.preferredMaxLayoutWidth = superview.bounds.size.width - 10
UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
[labelName sizeToFit];
labelName.numberOfLines = 0;
labelName.text = #"Your String...";
[self.view addSubview:labelName];
You can do that via the Storyboard too:
Select the Label on the view controller
In the Attribute Inspector, increase the value of the Line option (Press Alt+Cmd+4 to show Attributes Inspector)
Double click the Label in the view controller and write or paste your text
Resize the Label and/or increase the font size so that the whole text could be shown
you should try this:
-(CGFloat)dynamicLblHeight:(UILabel *)lbl
{
CGFloat lblWidth = lbl.frame.size.width;
CGRect lblTextSize = [lbl.text boundingRectWithSize:CGSizeMake(lblWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:lbl.font}
context:nil];
return lblTextSize.size.height;
}
Oh, in 2021 I'm trapped by a label text unable to change lines for 1 hour, then realize I forget to set label's width, WTF.
let stepLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.text = "Put your device and computer under same Wi-Fi network."
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(stepLabel)
NSLayoutConstraint.activate([
stepLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stepLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stepLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7)
])
}
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 30)];
[textLabel sizeToFit];
textLabel.numberOfLines = 0;
textLabel.text = #"Your String...";
Already answered, but you can do it manually in the storyboard too. Under Attributes Inspector for the Label, you can change Line Breaks to Word Wrap (or character wrap).
In this function pass string that you want to assign in label and pass font size in place of self.activityFont and pass label width in place of 235, now you get label height according to your string.
it will work fine.
-(float)calculateLabelStringHeight:(NSString *)answer
{
CGRect textRect = [answer boundingRectWithSize: CGSizeMake(235, 10000000) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:self.activityFont} context:nil];
return textRect.size.height;
}
Set below either in code or in storyboard itself
Label.lineBreakMode = NSLineBreakByWordWrapping;
Label.numberOfLines = 0;
and please don't forget to set left, right, top and bottom constraints for label otherwise it won't work.
On C#, this worked for me inside UITableViewCell.
UILabel myLabel = new UILabel();
myLabel.Font = UIFont.SystemFontOfSize(16);
myLabel.Lines = 0;
myLabel.TextAlignment = UITextAlignment.Left;
myLabel.LineBreakMode = UILineBreakMode.WordWrap;
myLabel.MinimumScaleFactor = 1;
myLabel.AdjustsFontSizeToFitWidth = true;
myLabel.InvalidateIntrinsicContentSize();
myLabel.Frame = new CoreGraphics.CGRect(20, mycell.ContentView.Frame.Y + 20, cell.ContentView.Frame.Size.Width - 40, mycell.ContentView.Frame.Size.Height);
myCell.ContentView.AddSubview(myLabel);
I think the point here is:-
myLabel.TextAlignment = UITextAlignment.Left;
myLabel.LineBreakMode = UILineBreakMode.WordWrap;
myLabel.MinimumScaleFactor = 1;
myLabel.AdjustsFontSizeToFitWidth = true;
This code is returning size height according to text
+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
{
CGFloat result = font.pointSize+4;
if (text)
{
CGSize size;
CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, 999)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil];
size = CGSizeMake(frame.size.width, frame.size.height+1);
result = MAX(size.height, result); //At least one row
}
return result;
}