Alterar dinamicamente o tamanho da fonte do UILabel


188

Atualmente tenho um 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];

Ao longo da vida do meu aplicativo iOS, factLabel obtém vários valores diferentes. Alguns com frases múltiplas, outros com apenas 5 ou 6 palavras.

Como posso configurar o UILabelmodo para que o tamanho da fonte seja alterado para que o texto sempre caiba nos limites que eu defini?


2
Para 2016, acredito realmente que a única boa solução é usar a abordagem "usar autoshrinking". Torne a caixa UILabel o tamanho real desejado, faça com que a fonte preencha o UILabel, selecione o auto-link, defina um tamanho de fonte enorme (300) e certifique-se de testar nos menores / maiores simuladores. (Portanto, atualmente 4s / PadPro.) Explicação completa: stackoverflow.com/a/35154493/294884 Esta é a única solução real hoje.
— Fattie

Respostas:


370

Única linha:

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

O código acima ajustará o tamanho da fonte do seu texto para (por exemplo) 8tentar ajustá-lo ao rótulo. numberOfLines = 1é mandatório.

Várias linhas:

Pois numberOfLines > 1existe um método para descobrir o tamanho do texto final através dos métodos de adição sizeWithFont do NSString: ... UIKit , por exemplo:

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

Depois disso, você pode redimensionar seu rótulo usando o resultado lLabelSize, por exemplo (supondo que você altere apenas a altura do rótulo):

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

iOS6

Única linha:

A partir do iOS6, minimumFontSizefoi preterido. A linha

factLabel.minimumFontSize = 8.;

pode ser alterado para:

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

iOS7

Várias linhas:

A partir do iOS7, sizeWithFontfica obsoleto. A caixa multilinha é reduzida para:

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

mas isso coloca o texto todo em uma linha. e se eu alterar o factLabel.numberOfLines, o tamanho da fonte não será alterado dinamicamente.
— CodeGuy

@ reising1: você está certo. É assim que fazer com que a estrutura para redimensionar funcione para você.
— Martin Babacaev

então a resposta para minha pergunta é que não há como fazê-lo usando a estrutura fornecida?
— CodeGuy

1
@ reising1: Neste caso, você também pode usar o método de NSString UIKit disso: sizeWithFont:constrainedToSize:lineBreakMode:Mas desta forma é um pouco difícil
— Martin Babacaev

6
Está obsoleto desde o iOS6. Substitua-o pormyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
— Norbert

72

minimumFontSizefoi descontinuado com o iOS 6. Você pode usar minimumScaleFactor.

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

Isso cuidará do tamanho da fonte, de acordo com a largura do rótulo e do texto.


Eu costumo usar 0,8, porque mesmo 0,7 tende a parecer muito pequeno. É claro que alguns textos podem não se encaixar no fator de escala mínimo 0.8, é uma questão de decidir o que parece melhor e onde as coisas ficam ilegíveis. OTOH meus aplicativos podem ser girados, o que ajuda muito.
— gnasher729

adjustsFontSizeToFitWidthsó reduz texto se ele não se encaixa dentro do recipiente
— USER25

24

Com base na resposta de @Eyal Ben Dov, convém criar uma categoria para torná-la flexível para uso em outros aplicativos seus.

Obs .: atualizei seu código para torná-lo compatível com o iOS 7

-Arquivo de cabeçalho

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Arquivo de implementação

#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

-Uso

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Felicidades


Não está funcionando para mim. O conteúdo do meu UILabel está cortado agora.
— Adrian

1
Se não estiver funcionando para você, provavelmente é porque o quadro do rótulo ainda não está definido. Tente definir o quadro antes de chamar isso (ou ligue setNeedsLayout/ layoutIfNeededse você estiver usando o AutoLayout).
— bmueller

Ele fornece a seguinte falha "'NSInvalidArgumentException', motivo: 'NSConcreteAttributedString initWithString :: nil value'"
— Mohamed Saleh

Isso significa que seu NSString não pode ser nulo. Estou assumindo que, se você deseja ajustar o tamanho da fonte para preencher o conteúdo do UILabel, você precisa fornecer pelo menos um texto.
— Paulo Miguel Almeida

Isso tem uma desvantagem. Ele quebra de linha entre caracteres, para que você veja as palavras divididas em linhas diferentes. Existe uma maneira de contornar isso?
— 21416

23

Linha única - Existem duas maneiras, você pode simplesmente mudar.

1- Pragmaticamente (Swift 3)

Basta adicionar o seguinte código

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

2 - Usando o inspetor UILabel Attributes

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)

insira a descrição da imagem aqui


22

É 2015. Eu tive que procurar uma postagem no blog que explicasse como fazer isso para a versão mais recente do iOS e XCode com Swift, para que funcionasse com várias linhas.

  1. defina "Autoshrink" para "Tamanho mínimo da fonte".
  2. defina a fonte para o maior tamanho de fonte desejado (escolhi 20)
  3. Altere "Quebras de linha" de "Quebra de linha" para "Truncar cauda".

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


Este é realmente um salva-vidas!
— bhakti123

2
Super legal .. Esse ponto de cauda truncado é o mais importante. Porque no caso de um layout automático de quebra de linha, não há necessidade de diminuir o tamanho da fonte, enquanto que, quando é um layout automático de truncado, é necessário salvar o texto da folha e é então que ele redimensiona a fonte.
— GKK

12

Versão rápida:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Thanks .. Parece que aqui seqüência também maters
— Pramod

7

Aqui está uma extensão do Swift para o UILabel. Ele executa um algoritmo de pesquisa binária para redimensionar a fonte com base na largura e altura dos limites do rótulo. Testado para funcionar com iOS 9 e autolayout.

USO: Onde <label>está o seu UILabel predefinido que precisa ser redimensionado

<label>.fitFontForSize()

Por padrão, essa função pesquisa dentro dos tamanhos de fonte 5pt e 300pt e define a fonte para caber seu texto "perfeitamente" dentro dos limites (preciso em 1,0pt). Você pode definir os parâmetros para que, por exemplo, pesquise entre 1pt e o tamanho da fonte atual do rótulo com precisão dentro de 0,1pts da seguinte maneira:

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

Copie / cole o seguinte código no seu arquivo

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
    }

}

NOTA: Cada uma das layoutIfNeeded()chamadas pode ser removida a seu critério


Ah - mas realmente não funciona com o layout automático; os "sizeToFit" não fazem nada nesse caso.
— Fattie

4

Não é um pouco sofisticado, mas isso deve funcionar, por exemplo, digamos que você queira limitar seu rótulo uil para 120x120, com tamanho máximo de fonte 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;
        }
    }

Isso parece bastante ineficiente - você deve deixar o UILabel se elevar dinamicamente para caber em algum espaço disponível fornecido. Se você executar isso para algo como o cálculo da fonte de título da célula de exibição de tabela, você terá grandes problemas de atraso. A abordagem pode funcionar, mas definitivamente não é recomendada.
— Zorayr

Voto positivo por ser a única pessoa a realmente responder à pergunta.
— Jai

2

Basta enviar a mensagem sizeToFit para o UITextView. Ele ajustará sua própria altura para caber apenas no texto. Não mudará sua própria largura ou origem.

[textViewA1 sizeToFit];

O que acontece quando o tamanho que cabe no texto é muito grande para o espaço do contêiner? Por exemplo, digamos que você tenha 100 pontos disponíveis para se ajustar à exibição de texto, depois de chamar sizeToFitseu número de textViewA1200 pontos, que acaba sendo cortado.
— Zorayr

0

Versão 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)
}

0

Esta solução funciona para várias linhas:

Depois de seguir vários artigos e exigir uma função que dimensionasse automaticamente o texto e ajustasse a contagem de linhas para melhor se ajustar ao tamanho especificado da etiqueta, eu mesmo escrevi uma função. (ou seja, uma corda curta caberia muito bem em uma linha e usaria uma grande quantidade do quadro da etiqueta, enquanto uma corda forte seria dividida automaticamente em 2 ou 3 linhas e ajustaria o tamanho de acordo)

Sinta-se livre para reutilizá-lo e ajustar conforme necessário. Certifique-se de chamá-lo após a viewDidLayoutSubviewsconclusão, para que o quadro de etiqueta inicial tenha sido definido.

+ (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;
}

0

Aqui está o código de preenchimento de uma subclasse UILabel que implementa a alteração do tamanho da fonte animada:

@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];
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.