【问题标题】:Resizing UILabel to fit with Word Wrap调整 UILabel 的大小以适应自动换行
【发布时间】:2010-08-19 10:30:35
【问题描述】:

这是 iPhone 应用程序的一部分,但通常应该适用于用 objC 编写的 Cocoa。

我有一个 UILabel 包含不同数量的文本(从单个字符到几个句子)。文本应始终以适合 UILabel 中所有文本的最大可能字体显示。 最大行数设置为4,换行模式设置为自动换行。

由于使用了多行,adjustsFontSizeToFitWidth 无法调整文本大小。

因此我使用循环来确定每个字符串的最大可能字体大小:

    //Set the text  
    self.textLabel.text = text;
    //Largest size used  
    NSInteger fsize = 200;  textLabel.font = [UIFont
    fontWithName:@"Verdana-Bold"
    size:fsize];

    //Calculate size of the rendered string with the current parameters
    float height = [text sizeWithFont:textLabel.font
        constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
        lineBreakMode:UILineBreakModeWordWrap].height;

    //Reduce font size by 5 while too large, break if no height (empty string)
    while (height > textLabel.bounds.size.height and height != 0) {   
        fsize -= 5;  
        textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];   
        height = [text sizeWithFont:textLabel.font 
            constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
            lineBreakMode:UILineBreakModeWordWrap].height;
    };

这种方法在大多数情况下效果很好。 长词除外。 让我们使用字符串@“体验 foo”。举个例子。 “经验”一词比其他词长得多,将在没有正确换行的情况下分成两半,字符串分成 4 行。 我正在寻找一种方法来进一步减小大小,以便每个单词都适合一行。

例子:

-老-

字体大小:60

The
Exper
ience
foo

应该是

-新-

字体大小:30

The
Experience
foo

可能有一种简单的方法可以做到这一点,但我碰壁了。

【问题讨论】:

    标签: iphone cocoa-touch uikit uilabel word-wrap


    【解决方案1】:

    这是我发现的最优雅(但有点老套)的方法:

    1. 将字符串拆分为单词
    2. 使用当前字体大小计算每个单词的宽度
    3. 减小字符串的大小,直到每个单词都适合一行

    资源消耗足够低,即使在UITableViews 充满了以这种方式编辑的字符串。

    这是新代码:

    //Set the text  
    self.textLabel.text = text;
    //Largest size used  
    NSInteger fsize = 200;  textLabel.font = [UIFont fontWithName:@"Verdana-Bold"
                                                             size:fsize];
    
    //Calculate size of the rendered string with the current parameters
    float height = 
          [text sizeWithFont:textLabel.font
           constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
               lineBreakMode:UILineBreakModeWordWrap].height;
    
    //Reduce font size by 5 while too large, break if no height (empty string)
    while (height > textLabel.bounds.size.height and height != 0) {   
        fsize -= 5;  
        textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];   
        height = [text sizeWithFont:textLabel.font 
                  constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
                      lineBreakMode:UILineBreakModeWordWrap].height;
    };
    
    // Loop through words in string and resize to fit
    for (NSString *word in [text componentsSeparatedByString:@" "]) {
        float width = [word sizeWithFont:textLabel.font].width;
        while (width > textLabel.bounds.size.width and width != 0) {
            fsize -= 3;
            textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];
            width = [word sizeWithFont:textLabel.font].width;
    
        }
    }
    

    【讨论】:

    • 因为sizeWithFont: 已被iOS 7 弃用,您应该将其替换为boundingRectWithSize:
    【解决方案2】:

    这是我的 0x90 在一个类别中的答案:

    @implementation UILabel (MultilineAutosize)
    
    - (void)adjustFontSizeToFit
    {
        //Largest size used
        NSInteger fsize = self.font.pointSize;
    
        //Calculate size of the rendered string with the current parameters
        float height = [self.text sizeWithFont:self.font
                             constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT)
                                 lineBreakMode:NSLineBreakByWordWrapping].height;
    
        //Reduce font size by 5 while too large, break if no height (empty string)
        while (height > self.bounds.size.height && height > 0) {
            fsize -= 5;
            self.font = [self.font fontWithSize:fsize];
            height = [self.text sizeWithFont:self.font
                           constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT)
                               lineBreakMode:NSLineBreakByWordWrapping].height;
        };
    
        // Loop through words in string and resize to fit
        for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
            float width = [word sizeWithFont:self.font].width;
            while (width > self.bounds.size.width && width > 0) {
                fsize -= 3;
                self.font = [self.font fontWithSize:fsize];
                width = [word sizeWithFont:self.font].width;
            }
        }
    }
    
    @end
    

    【讨论】:

      【解决方案3】:

      您可以在 UILabel 的类别中使用上面的代码

      UILabel+AdjustFontSize.h

      @interface UILabel (UILabel_AdjustFontSize)
      
      - (void) adjustsFontSizeToFitWidthWithMultipleLinesFromFontWithName:(NSString*)fontName size:(NSInteger)fsize andDescreasingFontBy:(NSInteger)dSize;
      
      @end
      

      UILabel+AdjustFontSize.m

      @implementation UILabel (UILabel_AdjustFontSize)
      
      - (void) adjustsFontSizeToFitWidthWithMultipleLinesFromFontWithName:(NSString*)fontName size:(NSInteger)fsize andDescreasingFontBy:(NSInteger)dSize{
      
          //Largest size used  
          self.font = [UIFont fontWithName:fontName size:fsize];
      
          //Calculate size of the rendered string with the current parameters
          float height = [self.text sizeWithFont:self.font
                          constrainedToSize:CGSizeMake(self.bounds.size.width,99999) 
                              lineBreakMode:UILineBreakModeWordWrap].height;
      
          //Reduce font size by dSize while too large, break if no height (empty string)
          while (height > self.bounds.size.height && height != 0) {   
              fsize -= dSize;
              self.font = [UIFont fontWithName:fontName size:fsize];   
              height = [self.text sizeWithFont:self.font 
                        constrainedToSize:CGSizeMake(self.bounds.size.width,99999) 
                            lineBreakMode:UILineBreakModeWordWrap].height;
          };
      
          // Loop through words in string and resize to fit
          for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
              float width = [word sizeWithFont:self.font].width;
              while (width > self.bounds.size.width && width != 0) {
                  fsize -= dSize;
                  self.font = [UIFont fontWithName:fontName size:fsize];
                  width = [word sizeWithFont:self.font].width;            
              }
          }
      }
      
      @end
      

      【讨论】:

      • 试过你的代码,在while语句中进入无限循环。
      【解决方案4】:

      这是一个很好的问题,您会认为现在使用尽可能大的字体而不打断单词将是内置UIKit 功能或相关框架的一部分。这是这个问题的一个很好的视觉示例:

      正如其他人所描述的,诀窍是对单个单词以及整个文本执行大小搜索。这是因为当您指定将单个单词绘制到其中的宽度时,大小调整方法会将单词分解,因为它们别无选择 - 您要求它们将具有特定字体大小的“牢不可破”的字符串绘制到一个区域中这根本不适合。

      在我的工作解决方案的核心,我使用以下二进制搜索功能:

      func binarySearch(string: NSAttributedString, minFontSize: CGFloat, maxFontSize: CGFloat, maxSize: CGSize, options: NSStringDrawingOptions) -> CGFloat {
          let avgSize = roundedFontSize((minFontSize + maxFontSize) / 2)
          if avgSize == minFontSize || avgSize == maxFontSize { return minFontSize }
          let singleLine = !options.contains(.usesLineFragmentOrigin)
          let canvasSize = CGSize(width: singleLine ? .greatestFiniteMagnitude : maxSize.width, height: .greatestFiniteMagnitude)
          if maxSize.contains(string.withFontSize(avgSize).boundingRect(with: canvasSize, options: options, context: nil).size) {
            return binarySearch(string: string, minFontSize:avgSize, maxFontSize:maxFontSize, maxSize: maxSize, options: options)
          } else {
            return binarySearch(string: string, minFontSize:minFontSize, maxFontSize:avgSize, maxSize: maxSize, options: options)
          }
        }
      

      仅此还不够。您需要使用它首先找到适合边界内最长单词的最大大小。一旦你有了它,继续搜索更小的尺寸,直到整个文本适合。这样一来,任何词都不会被打断。还有一些额外的考虑因素涉及更多,包括找出最长的单词实际上是什么(有一些陷阱!)和 iOS 字体缓存性能。

      如果您只关心以简单的方式在屏幕上显示文本,我已经在 Swift 中开发了一个强大的实现,我也在生产应用程序中使用它。这是一个UIView 子类,对任何输入文本(包括多行)具有高效、自动的字体缩放功能。要使用它,您只需执行以下操作:

      let view = AKTextView()
      // Use a simple or fancy NSAttributedString
      view.attributedText = .init(string: "Some text here")
      // Add to the view hierarchy somewhere
      

      就是这样!你可以在这里找到完整的源代码:https://github.com/FlickType/AccessibilityKit

      希望这会有所帮助!

      【讨论】:

        【解决方案5】:

        Swift 4 中的 UILabel 扩展基于 0x90 的回答:

        func adjustFontSizeToFit() {
            guard var font = self.font, let text = self.text else { return }
            let size = self.frame.size
            var maxSize = font.pointSize
            while maxSize >= self.minimumScaleFactor * self.font.pointSize {
                font = font.withSize(maxSize)
                let constraintSize = CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude)
                let textRect = (text as NSString).boundingRect(with: constraintSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : font], context: nil)
                let labelSize = textRect.size
                if labelSize.height <= size.height {
                    self.font = font
                    self.setNeedsLayout()
                    break
                }
                maxSize -= 1
            }
            self.font = font;
            self.setNeedsLayout()
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-05
          • 1970-01-01
          • 2012-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-25
          • 1970-01-01
          相关资源
          最近更新 更多