如何动态计算UILabel高度?


Answers:


85

尝试这个

// 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

要么

int lines = labelHeight/16;

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

要么

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

通过使用类别,只需创建名为的类别类

UILabel + UILabelDynamicHeight.h

UILabel + UILabelDynamicHeight.m

不再需要高度计算。请查看以下实施。

iOS7及更高版本,iOS 7以下的更新:动态计算UILabel高度

#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

但是如何找到标签的行数?
Hitesh,

1
我已经在第一个代码序列中指定了。请看“ // 16是字体大小”。如果字体大小不同,则标签的大小应更大。
Vijay-Apple-Dev.blogspot.com

@AppleVijay对不起,我还是要看一下,因为我只为+1提供了一个完美的代码。
达拉

1
@RayofHope乐意提供帮助:)
Vijay-Apple-Dev.blogspot.com

3
不推荐使用此方法。而是使用boundingRectWithSize:options:attributes:context
Abdul Yasin

57

调用-sizeToFitUILabel实例将自动调整其大小以适合其显示的文本,无需计算。如果需要大小,则可以从标签的frame属性中获取。

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));

但这并没有限制宽度以使UI布局受益。
shiami

2
@shiami它将宽度限制为调用-sizeToFit
Filip Radelic

为我工作,但需要尝试限制标签中的高度,需要放大或相等。
totalduka 2014年

1
如果您还记得.numberOfLines = 0属性,它将节省大量时间!谢谢!
卡门A

1
此外,您可能需要调用sizeToFitviewDidLayoutSubviews,而不是viewDidLoad
Chase Roberts

34

总而言之,您可以使用标签的字符串并调用来计算标签的高度boundingRectWithSize。您必须提供fontas作为属性,并包含.usesLineFragmentOrigin多行标签。

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)

一些扩展可以做到这一点:

迅捷版:

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版本:

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

@RoduckNickes无需太深入代码,我可以说,在尝试测量标签时,很有可能标签框/边界不正确。如果必须在此处进行测量,则可以通过调用setNeedsLayout和layoutIfNeeded来强制进行layoutSubviews调用。
kgaidis

@kgaidis是否可以修改,以便当标签的文本包含\ n时仍可以正确计算?
user2363025

当uilabel的高度常数低于实际文本的高度常数时,这将不起作用
Ucdemir 18'Dec

17

从iOS 7开始不推荐使用当前解决方案。

这是更新的解决方案:

+ (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;
}

之所以这样设置我的解决方案,是因为我正在使用UITableViewCell并相对于标签将占用多少空间来动态调整单元格的大小。


3
而不是发送'nil'通行证'@ {NSFontAttributeName:label.font}'作为属性
Salman Zaidi

方法boundingRectWithSize:options:attributes:context:仅在iOS 7及更高版本中可用。
无限詹姆斯

8

无需调用sizeToFit,就可以通过非常即插即用的解决方案以数字方式完成所有操作:

+ (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;
}

我对动态分配高度的UITableViewCells经常使用它。

也可以解决属性问题@Salman Zaidi。


不适用于NSMutableAttributedString且不调用sizeToFit始终提供相同的高度。
bruno 2015年

使用Plain UILabel字体进行了测试:Apple SD Gothic Neo。不起作用
C0D3

6

复制并粘贴此方法并使用它就像:

[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;
    }

@saraman,您好:如果复制并粘贴代码,请同时提及代码的来源。
Anup Gupta

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

2

要获取NSAttributedString的高度,请在下面使用此函数。其中width - UILabelUITextView的宽度

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
}

要获取String的高度,请使用以下函数,该函数与之前的方法几乎相同:

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
}

1

您需要创建String的扩展并调用此方法

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)
}

您必须发送标签的宽度


0

就我而言,我为每个部分使用了固定大小的标头,但具有动态的单元格大小在每个标头中。单元格的高度取决于标签的高度。

使用:

tableView.estimatedRowHeight = SomeNumber
tableView.rowHeight = UITableViewAutomaticDimension

有效,在使用时:

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

当许多标头没有折叠时,当框架重新加载动画时会产生很多错误,例如标头复制(标头类型x在同一类型下)和怪异的动画,即使与type一起使用 .none标头(FYI,固定标头高度和单元格高度有效)。

解决方案是使用heightForRowAt回调并自行计算标签的高度(加上动画看起来好得多)。请记住,高度是首先被调用的。

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
}

0

这是我用于计算多行UILabel高度的扩展,它是上一个堆栈溢出帖子中的调整后片段:

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)
    }
}

0

如果您使用的UILabel具有属性,则可以尝试方法textRect(forBounds:limitedToNumberOfLines)

这是我的示例:

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)

我在操场上的结果:

在此处输入图片说明



-2

如果您希望标签采用动态行,则可以使用此标签

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));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.