【问题标题】:How to calculate UILabel height dynamically?如何动态计算 UILabel 高度?
【发布时间】:2011-11-02 16:37:24
【问题描述】:

我想根据给定的文本动态计算 UILabel 的行数和高度。

【问题讨论】:

    标签: iphone ios label uilabel


    【解决方案1】:

    要获得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
    }
    

    【讨论】:

      【解决方案2】:

      如果你使用的是带有属性的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)
      

      还有我在操场上的结果:

      【讨论】:

        【解决方案3】:

        要使 UILabel 适合动态内容,您可以在属性检查器中使用 lines 属性并将其设置为 0。

        而且您不需要为此进行任何编码。

        更多详情,您可以查看下面的演示视频

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

        【讨论】:

          【解决方案4】:

          总而言之,您可以通过使用其字符串并调用boundingRectWithSize 来计算标签的高度。您必须提供font 作为属性,并包含.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)
          

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

          Swift 版本:

          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 是否可以修改,以便当标签的文本包含 \n 时它仍然可以正确计算?
          • 当 uilabel 的高度常数低于实际文本可以发生的高度时,这不起作用
          【解决方案5】:

          你需要创建一个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)
          }
          

          您必须发送标签的宽度

          【讨论】:

            【解决方案6】:

            这是我用于计算多行 UILabel 高度的扩展,它是从以前的堆栈溢出帖子调整后的 sn-p:

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

            【讨论】:

              【解决方案7】:

              在我的例子中,我为每个部分使用了一个固定大小的标题,但每个标题中都有一个动态单元格大小。 单元格的高度,取决于标签的高度。

              合作:

              tableView.estimatedRowHeight = SomeNumber
              tableView.rowHeight = UITableViewAutomaticDimension
              

              有效使用时:

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

              当很多标题没有折叠时,会产生很多错误,例如标题重复(标题类型 x 低于同一类型)和框架重新加载动画时奇怪的动画,即使使用类型 .none(仅供参考) ,固定的标题高度和单元格高度有效)。

              解决方案是使用heightForRowAt 回调并自己计算标签的高度(加上动画看起来好多更好)。请记住,高度被称为 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
              }
              

              【讨论】:

                【解决方案8】:

                如果你想让标签采用动态线条,你可以使用这个

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

                【讨论】:

                  【解决方案9】:

                  复制并粘贴此方法并使用它喜欢:

                  [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,如果复制并粘贴代码,请同时注明代码的来源。
                  【解决方案10】:
                  CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX);
                  CGSize requiredSize = [lbl sizeThatFits:maxSize];
                  CGFloat height=requiredSize.height
                  

                  【讨论】:

                    【解决方案11】:

                    试试这个

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

                    【讨论】:

                    • 但是如何找到标签的行数?
                    • 我已经在第一个代码序列中指定了。请看一下“//16 是字体大小”。如果字体大小不同,那么标签的大小应该更大。大的东西在房间里需要很大的空间!!!
                    • @RayofHope 乐于助人:)
                    • 此方法已弃用。而是使用 boundingRectWithSize:options:attributes:context
                    【解决方案12】:

                    无需调用 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,始终给出相同的高度。
                    • 用普通的 UILabel 字体测试了这个:Apple SD Gothic Neo。没用。
                    【解决方案13】:

                    从 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 并根据标签占用的空间动态调整单元格的大小。

                    【讨论】:

                    • 而不是发送 'nil' 传递 '@{NSFontAttributeName:label.font}' 作为 attributes
                    • boundingRectWithSize:options:attributes:context: 方法仅适用于 iOS 7 及更高版本。
                    【解决方案14】:

                    在 UILabel 实例上调用 -sizeToFit 会自动调整它的大小以适应它显示的文本,无需计算。如果需要尺寸,之后可以从 label 的 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 将宽度限制为在调用 -sizeToFit 之前设置的当前值
                    • 对我有用,但需要尝试限制标签中的高度,需要放置专业或相等。
                    • 如果您还记得 .numberOfLines = 0 属性,可以节省大量时间!谢谢!
                    • 另外,您可能需要在viewDidLayoutSubviews 中调用sizeToFit 而不是viewDidLoad
                    猜你喜欢
                    • 1970-01-01
                    • 2018-01-11
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多