代码之家  ›  专栏  ›  技术社区  ›  0x90

调整UILabel的大小以适应换行符

  •  6
  • 0x90  · 技术社区  · 14 年前

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

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

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

    这种方法在大多数情况下都很有效。 长单词除外。 让我们以字符串@“the experience foo.”为例。 单词“experience”比其他单词长得多,在没有正确包装单词的情况下会被一分为二,字符串会被分成4行。 我正在寻找一种方法,以减少大小进一步使每个字适合在一行。

    例子:

    -旧的-

    字号:60

    The
    Exper
    ience
    foo
    

    应该是

    -新建-

    The
    Experience
    foo
    

    可能有一个简单的方法可以做到这一点,但我撞到了墙。

    4 回复  |  直到 14 年前
        1
  •  13
  •   Andrei Sfat systemfreund    10 年前

    以下是我发现的最优雅(但有点粗俗)的方法:

    1. 减小字符串的大小,直到每个单词都排成一行

    资源消耗是足够低的,即使在 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;
    
        }
    }
    
        2
  •  4
  •   elsurudo    11 年前

    下面是我对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
  •  3
  •   Humberto    13 年前

    UILabel+AdjustFontSize.h

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

    @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
    
        4
  •  1
  •   Kosta Eleftheriou    6 年前

    这是一个很好的问题,你会认为使用尽可能大的字体大小而不拆分单词将是内置的一部分 UIKit 功能或相关框架。下面是这个问题的一个很好的视觉例子:

    Font resizing animation

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

    在我的工作方案的核心,我使用以下二进制搜索函数:

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

    如果您只关心以一种简单的方式在屏幕上显示文本,那么我已经用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
  •  0
  •   Stefan S    5 年前

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