【问题标题】:How to find actual number of lines of UILabel?如何找到 UILabel 的实际行数?
【发布时间】:2015-03-22 10:10:07
【问题描述】:

在我用textfont 初始化UILabel 后,如何找到它的实际行数?我已将其numberOfLines 属性设置为0,因此它将扩展到需要多少行。但是,在我设置了text 之后,我怎么才能知道它最终得到了多少行呢?

我发现了类似的问题,但似乎没有一个提供简明的答案,而且在我看来,它必须非常容易得到它,而无需任何开销来处理boundingRectWithSizesizeWithFont,...

【问题讨论】:

    标签: ios uilabel


    【解决方案1】:

    这些都不适合我。下面是一个,

    斯威夫特 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
        }    
    }
    

    斯威夫特 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
        }
    
    }
    

    斯威夫特 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
        }
    
    }
    

    【讨论】:

    • 行应该向上取整。 let lines = textSize.height / charSize letlinesRounded = Int(ceil(lines)) returnlinesRounded
    • @TommySadiqHinrichsen 我认为这不会考虑在内, textSize.height/charSize 无论如何都应该等于一个精确的整数。你有没有它不会的场景?
    • 使用行间距时。我可以理解您的观点,但我认为更强大,因为我们都知道浮点数有时会发生奇怪的事情。
    • 是的,但我认为只有在计算 boudingRect 时指定了段落样式时才适用。我想我可以改变它,因为我认为它不会造成任何伤害。
    • frame.size.width 有时会返回 0,它会给出错误的 maxSize 并基于该 textSize。就我而言,将UIScreen.main.bounds.width 设置为 maxSize 的宽度解决了问题
    【解决方案2】:

    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)
    

    【讨论】:

    • 它给了我一行,可见行数为 5,但您的方法返回 6。
    • let zone = CGSize(width: intrinsicContentSize.width, height: CGFloat(MAXFLOAT)) let fittingHeight = Float(self.sizeThatFits(zone).height) return lroundf(fittingHeight / Float(font.lineHeight)) 为我计算正确的行数
    • 这是一个不错的答案,但如果您使用自动换行,它偶尔会给您错误的答案。下面@sam 的回答给出了最好的结果。
    • label.layoutIfNeeded() 是 AutoLayout 所必需的
    【解决方案3】:

    首先在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);
    

    【讨论】:

    • int charSize = lroundf(yourLabel.font.lineHeight);
    • 我认为你应该在除法 rHeight/charSize 之后四舍五入,四舍五入 rHeight 和 charSize 可能会导致丢失一行,在我的情况下。
    • 就像@BrooksHanes 提到的那样——前导与 lineHeight 不同的是 The leading value represents additional space between lines of text and is measured in points. (developer.apple.com/reference/uikit/uifont/1619026-leading)。
    • 另外——boundingRectWithSize 是 NSString 的一个方法。
    • lineHeight 确实有效,而leading 无效(至少对我而言)。更新了答案
    【解决方案4】:

    这是@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 之前减去它们。

    【讨论】:

      【解决方案5】:

      Swift 5.2

      让它对我有用的要点是调用 label.layoutIfNeeded(),因为我使用的是 autoLayout,否则它不起作用。

      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() 的重要性。

      【讨论】:

      • var 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)) }
      【解决方案6】:

      这里的其他答案不尊重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:,这是我发现here 的一个巧妙技巧),那么您必须记住在计算@ 时考虑这些插入987654329@以上。例如:CGSizeMake(self.frame.size.width - (self.insets.left + self.insets.right), CGFLOAT_MAX)

      【讨论】:

        【解决方案7】:

        这是 Swift3 代码 在这里,您可以使用 (MAXFLOAT) 定义 Int 值并获取文本大小的高度,使用该高度可以获得 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)")
        

        【讨论】:

        • 虽然欢迎使用此代码 sn-p,并且可能会提供一些帮助,但它会是 greatly improved if it included an explanation of howwhy 这解决了问题。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人!请edit您的答案添加解释,并说明适用的限制和假设。
        【解决方案8】:

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

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

        【讨论】:

          【解决方案9】:

          似乎官方开发者网站在 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
              }
          }
          

          【讨论】:

          • 这是最好的答案。其他答案未能正确考虑自动换行,这可能导致行数太短,因为它们有效地使用了字符换行。
          • 一个警告,请确保使用 NSAttributedString。如果您尝试仅从文本创建 NSTextStorage,您将得到随机结果。
          • 这在提供包含 RTL 文本的标签时效果不佳。它使文本左对齐。
          • 总是返回第 1 行的编号,
          【解决方案10】:

          跟进@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
          

          【讨论】:

          • leading 的 Apple 文档显示“改用 lineHeight 属性。”事实上,在我的UILabel 上,字体的leading 属性等于0。
          【解决方案11】:

          Xamarin iOS

          label.Text = text;
          
          var lineCount = 0;
          var textSize = new CGSize(label.Frame.Size.Width, float.MaxValue);
          var height = label.SizeThatFits(textSize).Height;
          var fontHeight = label.Font.LineHeight;
          
          lineCount = Convert.ToInt32(height / fontHeight);
          

          【讨论】:

            【解决方案12】:

            请注意,@kurt-j 的回答并不总是有效。在某些情况下,您必须手动提供标签的宽度。由于存在这些情况,因此最好有一个可选的宽度参数,即使您最终没有使用它。

            Swift 4.2:

            extension UILabel {
                func calculateMaxLines(actualWidth: CGFloat?) -> Int {
                    var width = frame.size.width
                    if let actualWidth = actualWidth {
                        width = actualWidth
                    }
                    let maxSize = CGSize(width: 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
                }    
            }
            

            【讨论】:

              【解决方案13】:
              let l = UILabel()
              l.numberOfLines = 0
              l.layer.frame.size.width = self.view.frame.width - 40 /*padding(20 + 20)*/
              
              l.font = UIFont(name: "BwModelica-Bold", size: 16.0)
              
              l.text = "Random Any length Text!!"
              
              let noOfLines = ceil(l.intrinsicContentSize.width / l.frame.size.width)
              
              let lbl_height = noOfLines * l.intrinsicContentSize.height
              

              这将是您的标签的确切动态高度和行数。编码愉快!!!

              【讨论】:

                【解决方案14】:

                Xamarin.iOS

                感谢上面提供的每个人的答案。

                这将获取可见行数。

                public static int VisibleLineCount(this UILabel label)
                {
                    var textSize = new CGSize(label.Frame.Size.Width, nfloat.MaxValue);
                    nfloat rHeight = label.SizeThatFits(textSize).Height;
                    nfloat charSize = label.Font.LineHeight;
                    return Convert.ToInt32(rHeight / charSize);
                }
                

                这将获取文本将在屏幕上占据的实际行数。

                public static int LineCount(this UILabel label)
                {
                    var maxSize = new CGSize(label.Frame.Size.Width, nfloat.MaxValue);
                    var charSize = label.Font.LineHeight;
                    var text = (label.Text ?? "").ToNSString();
                    var textSize = text.GetBoundingRect(maxSize, NSStringDrawingOptions.UsesLineFragmentOrigin, new UIStringAttributes() { Font = label.Font }, null);
                    return Convert.ToInt32(textSize.Height / charSize);
                }
                

                我发现对我的用例有用的辅助方法。

                public static bool IsTextTruncated(this UILabel label)
                {
                    if (label.Lines == 0)
                    {
                        return false;
                    }
                    return (label.LineCount() > label.Lines);
                 }
                

                要获得更准确的行数:

                • 使用font.lineHeight 而不是font.pointSize
                • round() 除法​​后的行数

                【讨论】:

                  【解决方案15】:

                  ⚠️ lineHeightleading 本身也不够。

                  font.lineHeight 是字形的高度,它从descender(最低字形的底部)到ascender(最高字形的顶部)。另外,请注意 lineHeight 可以为属性字符串覆盖。 font.leading 是(!)行之间的额外(可能是负数)空格(请参阅文档)。如果使用系统字体,则几乎每个磅值都会得到不同的前导值。

                  例如带有 5 行的标签的高度由 5 lineHeight 和 4 leading 组成(但前导值通常足够小以使上述解决方案有效,直到您开始使用大量行。

                  所以正确的(伪)公式应该是:

                  (frame.size.height + font.leading) / (font.lineHeight + font.leading)
                  

                  此外,如果属性字符串的附件太大(高于字体的ascender),那么它也会改变该行的行高。

                  【讨论】:

                    【解决方案16】:

                    斯威夫特 5.4 Fernando Cardenas 对 UILabel 扩展的重构解决方案

                    private extension UILabel {
                        var actualNumberOfLines: Int {
                            guard let text = self.text else {
                                return 0
                            }
                            layoutIfNeeded()
                            let rect = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
                            let labelSize = text.boundingRect(
                                with: rect,
                                options: .usesLineFragmentOrigin,
                                attributes: [NSAttributedString.Key.font: font as Any],
                                context: nil)
                            return Int(ceil(CGFloat(labelSize.height) / font.lineHeight))
                        }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2018-10-13
                      • 1970-01-01
                      • 2012-03-24
                      • 2017-04-05
                      • 1970-01-01
                      • 2011-08-12
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多