动态改变UILabel的字体大小

Posted

技术标签:

【中文标题】动态改变UILabel的字体大小【英文标题】:Dynamically changing font size of UILabel 【发布时间】:2011-06-19 10:10:44 【问题描述】:

我目前有一个UILabel

factLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 280, 100)];
factLabel.text = @"some text some text some text some text";
factLabel.backgroundColor = [UIColor clearColor];
factLabel.lineBreakMode = UILineBreakModeWordWrap;
factLabel.numberOfLines = 10;
[self.view addSubview:factLabel];

在我的 ios 应用程序的整个生命周期中,factLabel 获得了一堆不同的值。有些有多个句子,有些只有 5 或 6 个单词。

我如何设置UILabel 以改变字体大小以使文本始终符合我定义的范围?

【问题讨论】:

对于 2016 年,我真的相信唯一的 解决方案是使用“使用自动收缩”方法。将 UILabel 框设置为您想要的实际大小,使字体填充 UILabel,选择 autoshrink,设置名义上的大字体大小(300),并确保在最小/最大的模拟器上进行测试。 (所以,目前是 4s/PadPro。)完整解释:***.com/a/35154493/294884这是今天唯一真正的解决方案。 【参考方案1】:

单行:

factLabel.numberOfLines = 1;
factLabel.minimumFontSize = 8;
factLabel.adjustsFontSizeToFitWidth = YES;

上面的代码会将您的文本的字体大小调整为(例如)8,以尝试使您的文本适合标签内。 numberOfLines = 1 是必填项。

多行:

对于numberOfLines > 1,有一种方法可以通过NSString's sizeWithFont:... UIKit addition方法来计算最终文本的大小,例如:

CGSize lLabelSize = [yourText sizeWithFont:factLabel.font
                                  forWidth:factLabel.frame.size.width
                             lineBreakMode:factLabel.lineBreakMode];

之后,您可以使用生成的lLabelSize 调整标签大小,例如(假设您只更改标签的高度):

factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, factLabel.frame.size.width, lLabelSize.height);

iOS6

单行:

从 iOS6 开始,minimumFontSize 已被弃用。线

factLabel.minimumFontSize = 8.;

可以改为:

factLabel.minimumScaleFactor = 8./factLabel.font.pointSize;

iOS7

多行:

从 iOS7 开始,sizeWithFont 被弃用。 多行大小写简化为:

factLabel.numberOfLines = 0;
factLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGSize maximumLabelSize = CGSizeMake(factLabel.frame.size.width, CGFLOAT_MAX);
CGSize expectSize = [factLabel sizeThatFits:maximumLabelSize];
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, expectSize.width, expectSize.height);

iOS 13 (Swift 5):

label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5

【讨论】:

但这会将文本全部放在一行上。如果我改变factLabel.numberOfLines,那么字体大小不会动态改变。 @reising1:你是对的。这就是如何让框架为你做调整大小的工作。 那么我的问题的答案是没有办法使用提供的框架来做到这一点? @reising1:这种情况下你也可以使用NSString UIKit添加的方法:sizeWithFont:constrainedToSize:lineBreakMode:但是这个方法有点难 自 iOS6 起已弃用。将其替换为myLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];【参考方案2】:

只需将 sizeToFit 消息发送到 UITextView。它将调整自己的高度以适合其文本。它不会改变自己的宽度或原点。

[textViewA1 sizeToFit];

【讨论】:

当适合文本的大小对于容器的空间来说太大时会发生什么?例如,假设您有 100 个点可用于适合文本视图,在调用 sizeToFit 后,您的 textViewA1 变为 200 个点,最终被裁剪。【参考方案3】:

minimumFontSize 已在 iOS 6 中弃用。您可以使用minimumScaleFactor

yourLabel.adjustsFontSizeToFitWidth=YES;
yourLabel.minimumScaleFactor=0.5;

这将根据标签和文本的宽度处理您的字体大小。

【讨论】:

我通常使用 0.8,因为即使是 0.7 也会显得太小。当然,有些文本可能不适合最小比例因子 0.8,这是决定什么看起来更好以及什么地方变得不可读的问题。 OTOH 我的应用程序可以旋转,这很有帮助。 adjustsFontSizeToFitWidth 仅在不适合容器的情况下减少文本【参考方案4】:

它有点不复杂,但这应该可以, 例如,假设您想将 uilabel 限制为 120x120,最大字体大小为 28:

magicLabel.numberOfLines = 0;
magicLabel.lineBreakMode = NSLineBreakByWordWrapping;
...
magicLabel.text = text;
    for (int i = 28; i>3; i--) 
        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:(CGFloat)i] constrainedToSize:CGSizeMake(120.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        if (size.height < 120) 
            magicLabel.font = [UIFont systemFontOfSize:(CGFloat)i];
            break;
        
    

【讨论】:

这似乎相当低效 - 你应该让 UILabel 动态高度本身以适应一些提供的可用空间。如果您为表格视图单元格的标题字体计算之类的东西运行此程序,您将遇到严重的滞后问题。该方法可能有效,但绝对不推荐。 投票赞成成为唯一真正回答问题的人。【参考方案5】:

根据@Eyal Ben Dov 的回答,您可能希望创建一个类别,以便在您的其他应用中灵活使用。

Obs.:我已经更新了他的代码以兼容 iOS 7

-头文件

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-实现文件

#import "UILabel+DynamicFontSize.h"

@implementation UILabel (DynamicFontSize)

#define CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE 35
#define CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE 3

-(void) adjustFontSizeToFillItsContents

    NSString* text = self.text;

    for (int i = CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE; i>CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE; i--) 

        UIFont *font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
        NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@NSFontAttributeName: font];

        CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];

        if (rectSize.size.height <= self.frame.size.height) 
            self.font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
            break;
        
    



@end

-用法

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

干杯

【讨论】:

它对我不起作用。我的 UILabel 的内容现在被截断了。 如果它不适合您,可能是因为尚未设置标签的框架。在调用它之前尝试设置框架(如果您使用的是 AutoLayout,请调用 setNeedsLayout/layoutIfNeeded)。 它给出了以下崩溃“'NSInvalidArgumentException',原因:'NSConcreteAttributedString initWithString::nil value'” 表示你的 NSString 不能为 nil。我假设如果你想调整字体大小来填充 UILabel 的内容,你至少要提供一个文本。 这有一个缺点。它在字符之间换行,因此您会看到单词分成不同的行。有没有办法绕过这个?【参考方案6】:

Swift 版本:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

【讨论】:

谢谢.. 似乎这里的序列也很重要【参考方案7】:

现在是 2015 年。我不得不去找一篇博文,解释如何使用 Swift 为最新版本的 iOS 和 XCode 执行此操作,以便它可以处理多行代码。

    将“自动缩小”设置为“最小字体大小”。 将字体设置为所需的最大字体大小(我选择了 20) 将“换行符”从“自动换行”更改为“截断尾部”。

来源: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/

【讨论】:

超级酷.. 截断尾点是最重要的.. 因为在自动换行的情况下不会感觉到减小字体大小的冲动,而当它被截断时,自动布局必须保存刀片中的文本,然后调整字体大小。【参考方案8】:

这是 UILabel 的 Swift 扩展。它运行二进制搜索算法来根据标签边界的宽度和高度调整字体大小。经测试可与 iOS 9 和自动布局一起使用。

用法:&lt;label&gt; 是您预定义的需要调整字体大小的 UILabel

<label>.fitFontForSize()

默认情况下,此函数在 5pt 和 300pt 字体大小范围内搜索,并将字体设置为“完美”地适应其文本范围(精确到 1.0pt)。您可以定义参数,例如,通过以下方式在 0.1pts 内准确搜索 1pt标签的当前字体大小

<label>.fitFontForSize(1.0, maxFontSize: <label>.font.pointSize, accuracy:0.1)

将以下代码复制/粘贴到您的文件中

extension UILabel 

    func fitFontForSize(var minFontSize : CGFloat = 5.0, var maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) 
        assert(maxFontSize > minFontSize)
        layoutIfNeeded() // Can be removed at your own discretion
        let constrainedSize = bounds.size
        while maxFontSize - minFontSize > accuracy 
            let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
            font = font.fontWithSize(midFontSize)
            sizeToFit()
            let checkSize : CGSize = bounds.size
            if  checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width 
                minFontSize = midFontSize
             else 
                maxFontSize = midFontSize
            
        
        font = font.fontWithSize(minFontSize)
        sizeToFit()
        layoutIfNeeded() // Can be removed at your own discretion
    


注意:您可以自行决定删除每个 layoutIfNeeded() 调用

【讨论】:

啊——但它并不真正适用于自动布局;在这种情况下,“sizeToFit”什么也不做。【参考方案9】:

Swift 2.0 版本:

private func adapteSizeLabel(label: UILabel, sizeMax: CGFloat) 
     label.numberOfLines = 0
     label.lineBreakMode = NSLineBreakMode.ByWordWrapping
     let maximumLabelSize = CGSizeMake(label.frame.size.width, sizeMax);
     let expectSize = label.sizeThatFits(maximumLabelSize)
     label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, expectSize.width, expectSize.height)

【讨论】:

【参考方案10】:

此解决方案适用于多行:

在阅读了几篇文章之后,我需要一个可以自动缩放文本并调整行数以最适合给定标签大小的函数,我自己编写了一个函数。 (即短字符串会很好地放在一行上并使用大量标签框,而长字符串会自动拆分为 2 或 3 行并相应调整大小)

随意重复使用它并根据需要进行调整。确保在 viewDidLayoutSubviews 完成后调用它,以便设置初始标签框架。

+ (void)setFontForLabel:(UILabel *)label withMaximumFontSize:(float)maxFontSize andMaximumLines:(int)maxLines 
    int numLines = 1;
    float fontSize = maxFontSize;
    CGSize textSize; // The size of the text
    CGSize frameSize; // The size of the frame of the label
    CGSize unrestrictedFrameSize; // The size the text would be if it were not restricted by the label height
    CGRect originalLabelFrame = label.frame;

    frameSize = label.frame.size;
    textSize = [label.text sizeWithAttributes:@NSFontAttributeName:[UIFont systemFontOfSize: fontSize]];

    // Work out the number of lines that will need to fit the text in snug
    while (((textSize.width / numLines) / (textSize.height * numLines) > frameSize.width / frameSize.height) && (numLines < maxLines)) 
        numLines++;
    

    label.numberOfLines = numLines;

    // Get the current text size
    label.font = [UIFont systemFontOfSize:fontSize];
    textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                     attributes:@NSFontAttributeName : label.font
                                        context:nil].size;

    // Adjust the frame size so that it can fit text on more lines
    // so that we do not end up with truncated text
    label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, label.frame.size.width);

    // Get the size of the text as it would fit into the extended label size
    unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;

    // Keep reducing the font size until it fits
    while (textSize.width > unrestrictedFrameSize.width || textSize.height > frameSize.height) 
        fontSize--;
        label.font = [UIFont systemFontOfSize:fontSize];
        textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
                                            options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                         attributes:@NSFontAttributeName : label.font
                                            context:nil].size;
        unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
    

    // Set the label frame size back to original
    label.frame = originalLabelFrame;

【讨论】:

【参考方案11】:

这是实现动画字体大小更改的 UILabel 子类的填充代码:

@interface SNTextLayer : CATextLayer

@end

@implementation SNTextLayer

- (void)drawInContext:(CGContextRef)ctx 
    // We override this to make text appear at the same vertical positon as in UILabel
    // (otherwise it's shifted tdown)
    CGFloat height = self.bounds.size.height;
    float fontSize = self.fontSize;
    // May need to adjust this somewhat if it's not aligned perfectly in your implementation
    float yDiff = (height-fontSize)/2 - fontSize/10;

    CGContextSaveGState(ctx);
    CGContextTranslateCTM(ctx, 0.0, yDiff);
    [super drawInContext:ctx];
     CGContextRestoreGState(ctx);


@end

@interface SNAnimatableLabel ()

@property CATextLayer* textLayer;

@end

@interface SNAnimatableLabel : UILabel

- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration;

@end



@implementation SNAnimatableLabel


- (void)awakeFromNib 
    [super awakeFromNib];
    _textLayer = [SNTextLayer new];
    _textLayer.backgroundColor = self.backgroundColor.CGColor;
    _textLayer.foregroundColor = self.textColor.CGColor;
    _textLayer.font = CGFontCreateWithFontName((CFStringRef)self.font.fontName);
    _textLayer.frame = self.bounds;
    _textLayer.string = self.text;
    _textLayer.fontSize = self.font.pointSize;
    _textLayer.contentsScale = [UIScreen mainScreen].scale;
    [_textLayer setPosition: CGPointMake(CGRectGetMidX(_textLayer.frame), CGRectGetMidY(_textLayer.frame))];
    [_textLayer setAnchorPoint: CGPointMake(0.5, 0.5)];
    [_textLayer setAlignmentMode: kCAAlignmentCenter];
    self.textColor = self.backgroundColor;
    // Blend text with background, so that it doens't interfere with textlayer text
    [self.layer addSublayer:_textLayer];
    self.layer.masksToBounds = NO;


- (void)setText:(NSString *)text 
    _textLayer.string = text;
    super.text = text;


- (void)layoutSubviews 
    [super layoutSubviews];
    // Need to enlarge the frame, otherwise the text may get clipped for bigger font sizes
    _textLayer.frame = CGRectInset(self.bounds, -5, -5);


- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration 
    [CATransaction begin];
    [CATransaction setAnimationDuration:duration];
    _textLayer.fontSize = fontSize;
    [CATransaction commit];

【讨论】:

【参考方案12】:

单行- 有两种方式,你可以简单地改变。

1- 务实 (Swift 3)

只需添加以下代码

    yourLabel.numberOfLines = 1;
    yourLabel.minimumScaleFactor = 0.7;
    yourLabel.adjustsFontSizeToFitWidth = true;

2 - 使用 UILabel 属性检查器

i- Select your label- Set number of lines 1.
ii- Autoshrink-  Select Minimum Font Scale from drop down
iii- Set Minimum Font Scale value as you wish , I have set 0.7 as in below image. (default is 0.5)

【讨论】:

在 Interface Builder 中做这件事的麻烦要少得多,所以你提到这两种方法真是太好了!

以上是关于动态改变UILabel的字体大小的主要内容,如果未能解决你的问题,请参考以下文章

ios 运行时特征,动态改变控件字体大小

创建单一大小的动态 UILabel 字体

Swift font.withSize 不会改变 UILabel 上的字体大小

UILabel 动态获取字体大小 - swift 3

XIB 中的 UILabel 字体大小不会改变,而颜色会改变

更改 UILabel 的大小会更改其字体名称