How to calculate UILabel height dynamically?

IphoneIosLabelUilabel

Iphone Problem Overview


I want to calculate number of lines and height of UILabel dynamically from given text for same.

Iphone Solutions


Solution 1 - Iphone

Try this

// UILabel *myLabel;

CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font 
                            constrainedToSize:myLabel.frame.size 
                                lineBreakMode:NSLineBreakByWordWrapping];

CGFloat labelHeight = labelSize.height;


int lines = [myLabel.text sizeWithFont:myLabel.font 
                     constrainedToSize:myLabel.frame.size 
                         lineBreakMode:NSLineBreakByWordWrapping].height/16; 
             // '16' is font size

or

int lines = labelHeight/16;

NSLog(@"lines count : %i \n\n",lines);	

or

int lines = [myLabel.text sizeWithFont:myLabel.font 
                     constrainedToSize:myLabel.frame.size 
                         lineBreakMode:UILineBreakModeWordWrap].height /myLabel.font.pointSize; //fetching font size from font

By Using Categories, Just Create the category class named as

UILabel+UILabelDynamicHeight.h

UILabel+UILabelDynamicHeight.m

No more tension about the height calculation. Please review the below implementation.

Updates for iOS7 & Above,iOS 7 below : Dynamically calculate the UILabel height

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
#define iOS7_0 @"7.0"

UILabel+UILabelDynamicHeight.h

#import <UIKit/UIKit.h>
@interface UILabel (UILabelDynamicHeight)

#pragma mark - Calculate the size the Multi line Label
/*====================================================================*/

    /* Calculate the size of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */
 -(CGSize)sizeOfMultiLineLabel;

@end

UILabel+UILabelDynamicHeight.m

#import "UILabel+UILabelDynamicHeight.h"
@implementation UILabel (UILabelDynamicHeight)


#pragma mark - Calculate the size,bounds,frame of the Multi line Label
/*====================================================================*/

/* Calculate the size,bounds,frame of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */
-(CGSize)sizeOfMultiLineLabel{
    
    //Label text
    NSString *aLabelTextString = [self text];
    
    //Label font
    UIFont *aLabelFont = [self font];
    
    //Width of the Label
    CGFloat aLabelSizeWidth = self.frame.size.width;
    
    
    if (SYSTEM_VERSION_LESS_THAN(iOS7_0)) {
        //version < 7.0
        
        return [aLabelTextString sizeWithFont:aLabelFont
                            constrainedToSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                lineBreakMode:NSLineBreakByWordWrapping];
    }
    else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(iOS7_0)) {
        //version >= 7.0
        
        //Return the calculated size of the Label
        return [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                           attributes:@{
                                                        NSFontAttributeName : aLabelFont
                                                        }
                                              context:nil].size;
        
    }
    
    return [self bounds].size;
    
}
@end

Solution 2 - Iphone

Calling -sizeToFit on UILabel instance will automatically resize it to fit text it displays, no calculating required. If you need the size, you can get it from label's frame property after that.

label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text";
[label sizeToFit];
NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame));

Solution 3 - Iphone

To summarize, you can calculate the height of a label by using its string and calling boundingRectWithSize. You must provide the font as an attribute, and include .usesLineFragmentOrigin for multi-line labels.

let labelWidth = label.frame.width
let maxLabelSize = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)
let actualLabelSize = label.text!.boundingRect(with: maxLabelSize, options: [.usesLineFragmentOrigin], attributes: [.font: label.font], context: nil)
let labelHeight = actualLabelSize.height(withWidth:labelWidth)

Some extensions to do just that:

Swift Version:

extension UILabel {
    func textHeight(withWidth width: CGFloat) -> CGFloat {
        guard let text = text else {
            return 0
        }
        return text.height(withWidth: width, font: font)
    }

    func attributedTextHeight(withWidth width: CGFloat) -> CGFloat {
        guard let attributedText = attributedText else {
            return 0
        }
        return attributedText.height(withWidth: width)
    }
}

extension String {
    func height(withWidth width: CGFloat, font: UIFont) -> CGFloat {
        let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let actualSize = self.boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], attributes: [.font : font], context: nil)
        return actualSize.height
    }
}

extension NSAttributedString {
    func height(withWidth width: CGFloat) -> CGFloat {
        let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let actualSize = boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], context: nil)
        return actualSize.height
    }
}

Objective-C Version:

UILabel+Utility.h

#import <UIKit/UIKit.h>
@interface UILabel (Utility)
- (CGFloat)textHeightForWidth:(CGFloat)width;
- (CGFloat)attributedTextHeightForWidth:(CGFloat)width;
@end

UILabel+Utility.m

@implementation NSString (Utility)
- (CGFloat)heightForWidth:(CGFloat)width font:(UIFont *)font {
    CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX);
    CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;
    return actualSize.height;
}
@end

@implementation NSAttributedString (Utility)
- (CGFloat)heightForWidth:(CGFloat)width {
    CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX);
    CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
    return actualSize.height;
}
@end

@implementation UILabel (Utility)
- (CGFloat)textHeightForWidth:(CGFloat)width {
    return [self.text heightForWidth:width font:self.font];
}
- (CGFloat)attributedTextHeightForWidth:(CGFloat)width {
    return [self.attributedText heightForWidth:width];
}
@end

Solution 4 - Iphone

The current solution has been deprecated as of iOS 7.

Here is an updated solution:

+ (CGFloat)heightOfCellWithIngredientLine:(NSString *)ingredientLine
					   withSuperviewWidth:(CGFloat)superviewWidth
{
	CGFloat labelWidth					= superviewWidth - 30.0f;
    //    use the known label width with a maximum height of 100 points
	CGSize labelContraints				= CGSizeMake(labelWidth, 100.0f);
	
	NSStringDrawingContext *context		= [[NSStringDrawingContext alloc] init];
	
	CGRect labelRect					= [ingredientLine boundingRectWithSize:labelContraints
														options:NSStringDrawingUsesLineFragmentOrigin
													 attributes:nil
														context:context];
	
    //    return the calculated required height of the cell considering the label
	return labelRect.size.height;
}

The reason that my solution is set up like this is because I am using a UITableViewCell and resizing the cell dynamically relative to how much room the label will take up.

Solution 5 - Iphone

Without calling sizeToFit, you can do this all numerically with a very plug and play solution:

+ (CGFloat)heightForText:(NSString*)text font:(UIFont*)font withinWidth:(CGFloat)width {
    CGSize size = [text sizeWithAttributes:@{NSFontAttributeName:font}];
    CGFloat area = size.height * size.width;
    CGFloat height = roundf(area / width);
    return ceilf(height / font.lineHeight) * font.lineHeight;
}

I use it a lot for UITableViewCells that have dynamically allocated heights.

Solves the attributes problem as well @Salman Zaidi.

Solution 6 - Iphone

Copy & paste this method & used It like:

[lblText setFrame:CGRectMake(lblText.frame.origin.x, lblText.frame.origin.y, width, [self getLabelHeight:lblText])];

 - (CGFloat)getLabelHeight:(UILabel*)label
    {
        CGSize constraint = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);
        CGSize size;
        
        NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
        CGSize boundingBox = [label.text boundingRectWithSize:constraint
                                                      options:NSStringDrawingUsesLineFragmentOrigin
                                                   attributes:@{NSFontAttributeName:label.font}
                                                      context:context].size;
        
        size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
        
        return size.height;
    }

Solution 7 - Iphone

CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX);
CGSize requiredSize = [lbl sizeThatFits:maxSize];
CGFloat height=requiredSize.height

Solution 8 - Iphone

To get height for the NSAttributedString use this function below. Where width - the width of your UILabel or UITextView

func getHeight(for attributedString: NSAttributedString, font: UIFont, width: CGFloat) -> CGFloat {
    let textStorage = NSTextStorage(attributedString: attributedString)
    let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
    let layoutManager = NSLayoutManager()
    layoutManager.addTextContainer(textContainter)
    textStorage.addLayoutManager(layoutManager)
    textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}

To get height for String use this function, It is almost identical like the previous method:

func getHeight(for string: String, font: UIFont, width: CGFloat) -> CGFloat {
    let textStorage = NSTextStorage(string: string)
    let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
    let layoutManager = NSLayoutManager()
    layoutManager.addTextContainer(textContainter)
    textStorage.addLayoutManager(layoutManager)
    textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}

Solution 9 - Iphone

You need to create an extension of String and call this method

func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
    let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
    return ceil(boundingBox.height)
}

You must send the width of your label

Solution 10 - Iphone

If you are using a UILabel with attributes, you can try the method textRect(forBounds:limitedToNumberOfLines).

This is my example:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
label.numberOfLines = 0
label.text = "Learn how to use RxSwift and RxCocoa to write applications that can react to changes in your underlying data without you telling it to do so."

let rectOfLabel = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 0)
let rectOfLabelOneLine = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 1)
let heightOfLabel = rectOfLabel.height
let heightOfLine = rectOfLabelOneLine.height
let numberOfLines = Int(heightOfLabel / heightOfLine)

And my results on the Playground:

enter image description here

Solution 11 - Iphone

In my case, I was using a fixed size header for each section but with a dynamically cell size in each header. The cell's height, depends on the label's height.

Working with:

tableView.estimatedRowHeight = SomeNumber
tableView.rowHeight = UITableViewAutomaticDimension

Works but when using:

tableView.reloadSections(IndexSet(integer: sender.tag) , with: .automatic)

when a lot of headers are not collapsed, creates a lot of bugs such as header duplication (header type x below the same type) and weird animations when the framework reloads with animation, even when using with type .none (FYI, a fixed header height and cell height works).

The solution is making the use of heightForRowAt callback and calculate the height of the label by your self (plus the animation looks a lot better). Remember that the height is being called first.

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
    let object = dataDetailsController.getRowObject(forIndexPath: indexPath)
    let label = UILabel(frame: tableView.frame)
    let font = UIFont(name: "HelveticaNeue-Bold", size: 25)
    label.text = object?.name
    label.font = font
    label.numberOfLines = 0
    label.textAlignment = .center
    label.sizeToFit()
    let size = label.frame.height
    return Float(size) == 0 ? 34 : size
}

Solution 12 - Iphone

This is the extension I use for calculating multiline UILabel heights, it's an adjusted snippet from a previous stack overflow post:

extension UILabel {
    func estimatedHeight(forWidth: CGFloat, text: String, ofSize: CGFloat) -> CGFloat {
    
        let size = CGSize(width: forWidth, height: CGFloat(MAXFLOAT))
    
        let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    
        let attributes = [NSFontAttributeName: UIFont.systemFont(ofSize: ofSize)]
    
        let rectangleHeight = String(text).boundingRect(with: size, options: options, attributes: attributes, context: nil).height
    
        return ceil(rectangleHeight)
    }
}

Solution 13 - Iphone

To make UILabel fit the dynamic content you can use lines property in property inspector and set that as 0.

And you don't require to do any coding for this.

For more details you can check below demonstration video

https://www.youtube.com/watch?v=rVeDS_OGslU

Solution 14 - Iphone

if you want the label to take dynamic lines you may use this

label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text ";
[label sizeToFit];
NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHiteshView Question on Stackoverflow
Solution 1 - IphoneVijay-Apple-Dev.blogspot.comView Answer on Stackoverflow
Solution 2 - IphoneFilip RadelicView Answer on Stackoverflow
Solution 3 - IphonekgaidisView Answer on Stackoverflow
Solution 4 - IphoneInfinity JamesView Answer on Stackoverflow
Solution 5 - IphoneAlexKorenView Answer on Stackoverflow
Solution 6 - IphonesaramanView Answer on Stackoverflow
Solution 7 - Iphoneash999View Answer on Stackoverflow
Solution 8 - IphoneKiryl BelasheuskiView Answer on Stackoverflow
Solution 9 - IphoneDURGESHView Answer on Stackoverflow
Solution 10 - IphoneTuan NguyenView Answer on Stackoverflow
Solution 11 - IphoneOhadMView Answer on Stackoverflow
Solution 12 - IphoneSasa BlagojevicView Answer on Stackoverflow
Solution 13 - IphoneJuber KhanView Answer on Stackoverflow
Solution 14 - IphoneAmr AngryView Answer on Stackoverflow