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?

CodeGuy
fonte
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
Martin Babacaev
fonte
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.

Amit Singh
fonte
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

Paulo Miguel Almeida
fonte
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

Devendra Singh
fonte
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/

zavtra
fonte
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
Esqarrouth
fonte
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

Avi Frankl
fonte
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;
        }
    }
Eyal Ben Dov
fonte
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];
codercat
fonte
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)
}
Phil
fonte
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;
}
aaroncatlin
fonte
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];
}
videolist
fonte