如何找到UILabel的实际行数?

91

在我使用textfont初始化了一个UILabel后,如何找到它实际的行数?我已经将它的numberOfLines属性设置为0,以便它可以扩展为所需的行数。但是,如何找出我设置它的text后最终得到了多少行?

我找到了类似的问题,但没有一个提供简洁答案的,在我的看来,获取它而不需要使用boundingRectWithSizesizeWithFont等方式是非常容易的...

17个回答

94

这些方法都对我没用。下面这个方法有效:

Swift 4.2:

extension UILabel {
    func calculateMaxLines() -> Int {
        let maxSize = CGSize(width: frame.size.width, height: CGFloat(Float.infinity))
        let charSize = font.lineHeight
        let text = (self.text ?? "") as NSString
        let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
        let linesRoundedUp = Int(ceil(textSize.height/charSize)) 
        return linesRoundedUp
    }    
}

Swift 4/4.1:

extension UILabel {

    func calculateMaxLines() -> Int {
        let maxSize = CGSize(width: frame.size.width, height: CGFloat(Float.infinity))
        let charSize = font.lineHeight
        let text = (self.text ?? "") as NSString
        let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        let linesRoundedUp = Int(ceil(textSize.height/charSize)) 
        return linesRoundedUp
    }

}

Swift 3:

extension UILabel {

    func calculateMaxLines() -> Int {
        let maxSize = CGSize(width: frame.size.width, height: CGFloat(Float.infinity))
        let charSize = font.lineHeight
        let text = (self.text ?? "") as NSString
        let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
        let linesRoundedUp = Int(ceil(textSize.height/charSize)) 
        return linesRoundedUp
    }

}

当使用行间距时,我能理解你的观点,但我认为这种方法更加健壮,因为我们都知道浮点数有时会出现奇怪的问题。 - Tommy Sadiq Hinrichsen
是的,但我认为只有在计算 boudingRect 时指定了段落样式才会应用。我想我可以更改它,因为我看不到它可能会造成什么问题。 - Kurt J
它也计算不可见的线...如果你只需要可见的线,下一个答案更正确。https://dev59.com/oV4c5IYBdhLWcg3wSIgs#44086468 - user924
这是一个可以的答案,但如果你使用自动换行,它偶尔会给出错误的答案。下面@sam的答案给出了最佳结果。 - Robert Schmid
1
frame.size.width有时会返回0,导致maxSize不正确,进而影响到textSize。在我的情况下,将UIScreen.main.bounds.width设置为maxSize解决了这个问题。 - lmboom
显示剩余2条评论

80

Swift 5 (IOS 12.2)

获取标签渲染文本所需的最大行数,以避免截断。

extension UILabel {
    var maxNumberOfLines: Int {
        let maxSize = CGSize(width: frame.size.width, height: CGFloat(MAXFLOAT))
        let text = (self.text ?? "") as NSString
        let textHeight = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height
        let lineHeight = font.lineHeight
        return Int(ceil(textHeight / lineHeight))
    }
}
在限定边界内,获取标签可显示的最大行数。在将文本分配给标签后使用此属性。
extension UILabel {
    var numberOfVisibleLines: Int {
        let maxSize = CGSize(width: frame.size.width, height: CGFloat(MAXFLOAT))
        let textHeight = sizeThatFits(maxSize).height
        let lineHeight = font.lineHeight
        return Int(ceil(textHeight / lineHeight))
    }
}

使用方法

print(yourLabel.maxNumberOfLines)
print(yourLabel.numberOfVisibleLines)

7
它为我提供了另一行,可见行数为5,但是您的方法返回了6行。 - Markicevic
2
让我们来翻译一下这段程序相关的内容:let zone = CGSize(width: intrinsicContentSize.width, height: CGFloat(MAXFLOAT)) let fittingHeight = Float(self.sizeThatFits(zone).height) return lroundf(fittingHeight / Float(font.lineHeight)) 此代码用于计算正确的行数。 - Azules
这是一个可以的答案,但如果你使用自动换行,它偶尔会给出错误的答案。下面@sam的答案给出了最佳结果。 - Robert Schmid
1
label.layoutIfNeeded() 对于自动布局是必要的。 - Jeff

63

首先在UILabel中设置文本

第一个选项:

首先根据字体计算文本高度:

NSInteger lineCount = 0;
CGSize labelSize = (CGSize){yourLabel.frame.size.width, MAXFLOAT};
CGRect requiredSize = [self boundingRectWithSize:labelSize  options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: yourLabel.font} context:nil];

现在计算行数:

int charSize = lroundf(yourLabel.font.lineHeight);
int rHeight = lroundf(requiredSize.height);
lineCount = rHeight/charSize;
NSLog(@"No of lines: %i",lineCount);

第二个选项:

 NSInteger lineCount = 0;
 CGSize textSize = CGSizeMake(yourLabel.frame.size.width, MAXFLOAT);
 int rHeight = lroundf([yourLabel sizeThatFits:textSize].height);
 int charSize = lroundf(yourLabel.font.lineHeight);
 lineCount = rHeight/charSize;
 NSLog(@"No of lines: %i",lineCount);

5
int charSize = lroundf(yourLabel.font.lineHeight); int charSize = lroundf(yourLabel.font.lineHeight); - Brooks Hanes
1
我认为在除以rHeight/charSize之后应该四舍五入,将rHeight和charSize四舍五入可能会导致丢失一行,这在我的情况下就是这样。 - ambientlight
2
就像@BrooksHanes所提到的- 领先不同于行高,领先值代表文本行之间的额外间距,以点为单位测量。(https://developer.apple.com/reference/uikit/uifont/1619026-leading) - Chris Prince
lineHeight 确实有效,而 leading 不行(至少对我来说是这样)。回答已更新。 - yonix
这是一个可以的答案,但如果你使用自动换行,它偶尔会给出错误的答案。下面@sam的答案给出了最佳结果。 - Robert Schmid
显示剩余2条评论

22

这是@Paresh解决方案的Swift版本

func lines(label: UILabel) -> Int {
    let textSize = CGSize(width: label.frame.size.width, height: CGFloat(Float.infinity))
    let rHeight = lroundf(Float(label.sizeThatFits(textSize).height))
    let charSize = lroundf(Float(label.font.lineHeight))
    let lineCount = rHeight/charSize
    return lineCount
}

编辑:我不知道为什么,但是这段代码返回的行数比实际行数多了2行,对于我的解决方案,我只需在返回lineCount之前减去它们。


这个可以工作,但在调用此函数之前,请调用label.layoutIfNeeded(),如@Fernando Cardenas所述。 - Malik Kulsoom

15

Swift 5.2

对于我而言,让它起作用的关键是调用 label.layoutIfNeeded() ,因为我正在使用自动布局,否则它不起作用。

func actualNumberOfLines(label: UILabel) -> Int {
        // You have to call layoutIfNeeded() if you are using autoLayout
        label.layoutIfNeeded()

        let myText = label.text! as NSString

        let rect = CGSize(width: label.bounds.width, height: CGFloat.greatestFiniteMagnitude)
        let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: label.font as Any], context: nil)

        return Int(ceil(CGFloat(labelSize.height) / label.font.lineHeight))
    }

致谢:https://gist.github.com/fuxingloh/ccf26bb68f4b8e6cfd02,提供了旧版本Swift的解决方案,并提到了layoutIfNeeded()的重要性。


变量 actualNumberOfLines: Int { layoutIfNeeded() let myText = text! as NSString let rect = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude) let labelSize = myText.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font as Any], context: nil) return Int(ceil(CGFloat(labelSize.height) / font.lineHeight)) } - Bunthoeun

12
这里的其他答案没有尊重当UILabelnumberOfLines属性被设置为0以外的值时,这个属性是什么。
下面是你可以添加到你的类别或子类的另一个选项:
- (NSUInteger)lineCount
{
    CGSize size = [self sizeThatFits:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)];
    return MAX((int)(size.height / self.font.lineHeight), 0);
}

一些注意事项:

  • 我在使用带属性文本的 UILabel 上,从未实际设置过 font 属性,它正常工作。显然,如果您在您的 attributedText 中使用了多种字体,则可能会遇到问题。
  • 如果您正在子类化 UILabel 以具有自定义的边缘插图(例如通过覆盖 drawTextInRect:,这是我在 这里 找到的一种巧妙技巧),则必须记住在计算上面的 size 时考虑这些插图。例如:CGSizeMake(self.frame.size.width - (self.insets.left + self.insets.right), CGFLOAT_MAX)

10

这里是Swift3代码: 您可以定义Int值并使用(MAXFLOAT)获取文本大小的高度,然后使用该高度可以获取UILabel的总高度,通过将总高度除以字符大小,可以得到UILabel的实际行数。

var lineCount: Int = 0
var textSize = CGSize(width: CGFloat(yourLabel.frame.size.width), height: CGFloat(MAXFLOAT))
var rHeight: Int = lroundf(yourLabel.sizeThatFits(textSize).height)
var charSize: Int = lroundf(yourLabel.font.leading)
lineCount = rHeight / charSize
print("No of lines: \(lineCount)")

虽然这段代码片段是受欢迎的,可能会提供一些帮助,但如果它包含解释“如何”和“为什么”解决问题,那将会大大改善。请记住,你正在回答未来读者的问题,而不仅仅是现在提问的人!请编辑你的答案以添加解释,并指出适用的限制和假设。 - Toby Speight

7
似乎官方开发者网站提到了一个Objc解决方案Counting Lines of Text。然而,前提是您有配置了布局管理器、文本存储和文本容器的文本视图的引用。不幸的是,UILabel没有向我们公开这些内容,因此我们需要创建具有与UILabel相同配置的这些组件。
我已将Objc代码翻译为Swift,如下所示。在我看来,它似乎工作得很好。
extension UILabel {
    var actualNumberOfLines: Int {
        let textStorage = NSTextStorage(attributedString: self.attributedText!)
        let layoutManager = NSLayoutManager()
        textStorage.addLayoutManager(layoutManager)
        let textContainer = NSTextContainer(size: self.bounds.size)
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = self.lineBreakMode
        layoutManager.addTextContainer(textContainer)

        let numberOfGlyphs = layoutManager.numberOfGlyphs
        var numberOfLines = 0, index = 0, lineRange = NSMakeRange(0, 1)

        while index < numberOfGlyphs {
            layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
            index = NSMaxRange(lineRange)
            numberOfLines += 1
        }
        return numberOfLines
    }
}

1
这是最佳答案。其他答案未能正确解决单词换行问题,这可能导致行数过短,因为它们实际上使用的是字符换行。 - Robert Schmid
一个注意点是确保使用 NSAttributedString。如果你尝试从纯文本创建 NSTextStorage,会得到随机的结果。 - Robert Schmid
当提供一个从右到左的文本标签时,这种方法不能很好地工作,因为它会使文本左对齐。 - Yarneo
3
始终返回第一行的行数。 - Dhiru

6

您可以找到自定义标签中可用行的总数。 请查看此代码...

NSInteger numberOfLines = [self lineCountForText:@"YOUR TEXT"];

- (int)lineCountForText:(NSString *) text
{
    UIFont *font = [UIFont systemFontOfSize: 15.0];
    int width=Your_LabelWidht;

    CGRect rect = [text boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin  attributes:@{NSFontAttributeName : font} context:nil];
    return ceil(rect.size.height / font.lineHeight);
}

5

在@Prince的回答基础上,我现在按照以下方式在UILabel上实现了一个类别(请注意,我纠正了他的回答中一些语法错误,这些错误会导致代码无法编译):

UILabel+Util.h

#import <UIKit/UIKit.h>

@interface UILabel (Util)
- (NSInteger)lineCount;
@end

UILabel+Util.,

#import "UILabel+Util.h"

@implementation UILabel (Util)

- (NSInteger)lineCount
{
    // Calculate height text according to font
    NSInteger lineCount = 0;
    CGSize labelSize = (CGSize){self.frame.size.width, FLT_MAX};
    CGRect requiredSize = [self.text boundingRectWithSize:labelSize  options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.font} context:nil];

    // Calculate number of lines
    int charSize = self.font.leading;
    int rHeight = requiredSize.size.height;
    lineCount = rHeight/charSize;

    return lineCount;
}

@end

5
苹果关于“leading”的文档中写道:“使用lineHeight属性代替。” 实际上,在我的UILabel上,字体的“leading”属性等于0。 - Gerard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接