Ios 带adjustsFontSizeToFitWidth的多行UILabel

Ios 带adjustsFontSizeToFitWidth的多行UILabel,ios,iphone,cocoa-touch,uikit,uilabel,Ios,Iphone,Cocoa Touch,Uikit,Uilabel,我有一个多行UILabel,我想根据文本长度调整其字体大小。整个文本应适合标签的框架,而不会截断它 不幸的是,根据文档,只有当numberOfLines属性设置为1时,adjustsFontSizeToFitWidth属性“才有效” 我尝试使用 -[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode] 然后

我有一个多行UILabel,我想根据文本长度调整其字体大小。整个文本应适合标签的框架,而不会截断它

不幸的是,根据文档,只有当
numberOfLines
属性设置为1时,
adjustsFontSizeToFitWidth
属性“才有效”

我尝试使用

-[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode]
然后减小字体大小,直到合适为止。不幸的是,此方法在内部截断文本以适应指定的大小,并返回结果截断字符串的大小。

在中,0x90提供了一种解决方案,尽管有点难看,但它可以满足我的需要。具体来说,它正确地处理了单个单词与初始字体大小的宽度不匹配的情况。我稍微修改了代码,使其成为
NSString
上的一个类别:

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0) {   
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
        CGFloat width = [word sizeWithFont:newFont].width;
        while (width > size.width && width != 0) {
            fontSize--;
            newFont = [UIFont fontWithName:font.fontName size:fontSize];   
            width = [word sizeWithFont:newFont].width;
        }
    }
    return fontSize;
}
要将其与
ui标签一起使用

    CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
    label.font = [UIFont boldSystemFontOfSize:fontSize];

编辑:修复了使用
font
初始化
newFont
的代码。修复了某些情况下的崩溃。

谢谢,我从其他人那里获得了更多信息,我制作了这个自定义UILabel,它将尊重最小字体大小,并且有一个额外的选项可以将文本对齐到顶部

h:

m:


我希望能有所帮助。

在某些情况下,如果您知道需要多少行(例如“2”):请注意:

注释中提供了一个ObjC扩展,用于计算将多行文本放入UILabel所需的字体大小。 在Swift中重写(自2016年起):


链接到完整代码:

有关完整工作的解决方案,请参阅我的答案Swift 4.2的底部:
在我看来,这个问题现在有了更好的答案

我发现,当您将adjustsFontSizeToFitWidth设置为true、numberOfLines设置为0、最小缩放因子设置为足够小以允许字体根据需要收缩,以及设置默认的lineBreakMode(通过截断Tail)时,多行标签上的字体大小会自动调整


PS:我在官方文件中找不到任何关于这一变化的信息。我创建了一个问题,以了解有关此主题的更多信息

我想对答案添加我自己的观点:

我认为这比其他一些答案稍有改进,因为:

  • 在循环遍历单词时进行缓存,以按宽度查找最长的单词,以防止不必要的计算
  • 进行尺寸计算时,将使用所有标签属性
  • 注意:仅当UILabel具有
    .numberOfLines=0
    .lineBreakMode=.byWordWrapping

    否则,最好使用标准swift库中的:
    .adjustFontSizeForWidth=true


    这个帖子上的其他答案对我提出解决方案非常有帮助,谢谢你

    我发现使用
    [self components separatedbycharactersinset:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]
    比按空格拆分更准确。这很好。我在
    UILabel
    上添加了对最小字体大小和类别的支持,以方便感兴趣的人。这是一个很棒的方法。您可以考虑将断线模式添加为另一个参数。@ ObjjFlash,当然可能不是最好的样式。通常你会把它放在一个额外的NSString+Additions.h/.m文件中。然后你可以在任何需要的地方导入它。我尝试将其转换为iOS 7友好型,但无法得到相同的结果。iOS 7是否有类似的计量?但是,通过所有这些方法,如果您的文本在起始(现在最大)字体大小下无法放入指定的行数,constrainedToSize调用会根据您的换行符愉快地截断文本,以适应宽度。因此,你最终只能缩小以适应剩下的内容,而不是我认为你真正想要的内容,即“缩小以适应n行数的所有文本”
    @interface EPCLabel : UILabel {
        float originalPointSize;
        CGSize originalSize;
    }
    
    @property (nonatomic, readwrite) BOOL alignTextOnTop;
    @end
    
    #import "EPCLabel.h"
    
    @implementation EPCLabel
    @synthesize alignTextOnTop;
    
    -(void)verticalAlignTop {
        CGSize maximumSize = originalSize;
        NSString *dateString = self.text;
        UIFont *dateFont = self.font;
        CGSize dateStringSize = [dateString sizeWithFont:dateFont 
                                       constrainedToSize:CGSizeMake(self.frame.size.width, maximumSize.height)
                                           lineBreakMode:self.lineBreakMode];
    
        CGRect dateFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, dateStringSize.height);
    
        self.frame = dateFrame;
    }
    
    - (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
        CGFloat fontSize = [font pointSize];
        CGFloat height = [self.text sizeWithFont:font             
                               constrainedToSize:CGSizeMake(size.width,FLT_MAX)  
                                   lineBreakMode:UILineBreakModeWordWrap].height;
        UIFont *newFont = font;
    
        //Reduce font size while too large, break if no height (empty string)
        while (height > size.height && height != 0 && fontSize > self.minimumFontSize) { 
            fontSize--;  
            newFont = [UIFont fontWithName:font.fontName size:fontSize];   
            height = [self.text sizeWithFont:newFont  
                           constrainedToSize:CGSizeMake(size.width,FLT_MAX) 
                               lineBreakMode:UILineBreakModeWordWrap].height;
        };
    
        // Loop through words in string and resize to fit
        if (fontSize > self.minimumFontSize) {
            for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
                CGFloat width = [word sizeWithFont:newFont].width;
                while (width > size.width && width != 0 && fontSize > self.minimumFontSize) {
                    fontSize--;
                    newFont = [UIFont fontWithName:font.fontName size:fontSize];   
                    width = [word sizeWithFont:newFont].width;
                }
            }
        }
        return fontSize;
    }
    
    -(void)setText:(NSString *)text {
        [super setText:text];
        if (originalSize.height == 0) {
            originalPointSize = self.font.pointSize;
            originalSize = self.frame.size;
        }
    
        if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 1) {
            UIFont *origFont = [UIFont fontWithName:self.font.fontName size:originalPointSize];
            self.font = [UIFont fontWithName:origFont.fontName size:[self fontSizeWithFont:origFont constrainedToSize:originalSize]];
        }
    
        if (self.alignTextOnTop) [self verticalAlignTop];
    }
    
    -(void)setAlignTextOnTop:(BOOL)flag {
        alignTextOnTop = YES;
        if (alignTextOnTop && self.text != nil)
            [self verticalAlignTop];
    }
    
    @end
    
    //
    //  NSString+KBAdditions.swift
    //
    //  Created by Alexander Mayatsky on 16/03/16.
    //
    //  Original code from http://stackoverflow.com/a/4383281/463892 & http://stackoverflow.com/a/18951386
    //
    
    import Foundation
    import UIKit
    
    protocol NSStringKBAdditions {
        func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat
    }
    
    extension NSString : NSStringKBAdditions {
        func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat {
            var fontSize = font.pointSize
            let minimumFontSize = fontSize * minimumScaleFactor
    
    
            var attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: font])
            var height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height
    
            var newFont = font
            //Reduce font size while too large, break if no height (empty string)
            while (height > size.height && height != 0 && fontSize > minimumFontSize) {
                fontSize--;
                newFont = UIFont(name: font.fontName, size: fontSize)!
    
                attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: newFont])
                height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height
            }
    
            // Loop through words in string and resize to fit
            for word in self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) {
                var width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
                while (width > size.width && width != 0 && fontSize > minimumFontSize) {
                    fontSize--
                    newFont = UIFont(name: font.fontName, size: fontSize)!
                    width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
                }
            }
            return fontSize;
        }
    }
    
    //
    //  String+Utility.swift
    //
    //  Created by Philip Engberg on 29/11/2018.
    //  Original code from http://stackoverflow.com/a/4383281/463892 & http://stackoverflow.com/a/18951386
    //
    
    import Foundation
    import UIKit
    
    extension String {
        func fontSize(with font: UIFont, constrainedTo size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat {
            var fontSize = font.pointSize
            let minimumFontSize = fontSize * minimumScaleFactor
    
            var attributedText = NSAttributedString(string: self, attributes: [.font: font])
            var height = attributedText.boundingRect(with: CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin], context: nil).size.height
    
            var newFont = font
            //Reduce font size while too large, break if no height (empty string)
            while height > size.height && height != 0 && fontSize > minimumFontSize {
                fontSize -= 1
                newFont = UIFont(name: font.fontName, size: fontSize)!
    
                attributedText = NSAttributedString(string: self, attributes: [.font: newFont])
                height = attributedText.boundingRect(with: CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin], context: nil).size.height
            }
    
            // Loop through words in string and resize to fit
            for word in self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines) {
                var width = word.size(withAttributes: [.font: newFont]).width
                while width > size.width && width != 0 && fontSize > minimumFontSize {
                    fontSize -= 1
                    newFont = UIFont(name: font.fontName, size: fontSize)!
                    width = word.size(withAttributes: [.font: newFont]).width
                }
            }
            return fontSize
        }
    }