Cambio dinámico del tamaño de fuente de UILabel

188

Actualmente tengo un 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];

A lo largo de la vida de mi aplicación iOS, factLabelobtengo un montón de valores diferentes. Algunos con múltiples oraciones, otros con solo 5 o 6 palabras.

¿Cómo puedo configurar UILabelpara que el tamaño de la fuente cambie y que el texto siempre se ajuste a los límites que definí?

CodeGuy
fuente
2
Para 2016, realmente creo que la única buena solución es usar el enfoque de "usar autoencogimiento". Haga que el cuadro UILabel tenga el tamaño real que desea, haga que la fuente llene el UILabel, seleccione el encogimiento automático, establezca un tamaño de fuente enorme titular (300) y asegúrese de probar en los simuladores más pequeños / más grandes. (Entonces, 4s / PadPro actualmente.) Explicación completa: stackoverflow.com/a/35154493/294884 Esta es la única solución real hoy en día.
Fattie

Respuestas:

370

Linea sola:

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

El código anterior ajustará el tamaño de fuente de su texto a (por ejemplo) 8tratando de ajustar su texto dentro de la etiqueta. numberOfLines = 1es obligatorio.

Múltiples lineas:

Porque numberOfLines > 1hay un método para determinar el tamaño del texto final a través de sizeWithFont de NSString: ... métodos de adición UIKit , por ejemplo:

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

Después de eso, puede cambiar el tamaño de su etiqueta usando el resultado lLabelSize, por ejemplo (suponiendo que cambiará solo la altura de la etiqueta):

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

ios 6

Linea sola:

Comenzando con iOS6, minimumFontSizeha quedado en desuso. La línea

factLabel.minimumFontSize = 8.;

se puede cambiar a:

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

ios 7

Múltiples lineas:

Comenzando con iOS7, se sizeWithFontvuelve obsoleto. El caso multilínea se reduce a:

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
fuente
pero esto pone todo el texto en una línea. y si cambio el factLabel.numberOfLines, entonces el tamaño de la fuente no cambia dinámicamente.
CodeGuy
@ reising1: tienes razón. Esta es solo la forma de hacer que el marco haga el trabajo de cambio de tamaño para usted.
Martin Babacaev
Entonces, ¿la respuesta a mi pregunta es que no hay forma de hacerlo utilizando el marco proporcionado?
CodeGuy 01 de
1
@ reising1: en este caso, también puede utilizar el método de adición de NSString UIKit: sizeWithFont:constrainedToSize:lineBreakMode:pero de esta manera es un poco difícil
Martin Babacaev
66
Está en desuso desde iOS6. Reemplácelo conmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert
72

minimumFontSizeha quedado en desuso con iOS 6. Puede usarlo minimumScaleFactor.

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

Esto se encargará de su tamaño de fuente según el ancho de la etiqueta y el texto.

Amit Singh
fuente
Usualmente uso 0.8, porque incluso 0.7 tiende a parecer demasiado pequeño. Por supuesto, algunos textos pueden no ajustarse al factor de escala mínimo 0.8, se trata de decidir qué se ve mejor y dónde las cosas se vuelven ilegibles. OTOH mis aplicaciones se pueden rotar, lo que ayuda mucho.
gnasher729
adjustsFontSizeToFitWidthsolo reduce el texto si no cabe dentro del contenedor
usuario25
24

Según la respuesta de @Eyal Ben Dov, es posible que desee crear una categoría para que sea flexible de usar dentro de otras aplicaciones suyas.

Obs .: He actualizado su código para que sea compatible con iOS 7

-Archivo de cabecera

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Archivo de implementación

#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];

Salud

Paulo Miguel Almeida
fuente
No está funcionando para mi. El contenido de mi UILabel está cortado ahora.
Adrian
1
Si no funciona para usted, probablemente se deba a que el marco de la etiqueta aún no está configurado. Intente configurar el marco antes de llamar a esto (o llame setNeedsLayout/ layoutIfNeededsi está usando AutoLayout).
bmueller
Da el siguiente bloqueo "'NSInvalidArgumentException', razón: 'NSConcreteAttributedString initWithString :: valor nulo'"
Mohamed Saleh
Significa que su NSString no puede ser nulo. Supongo que si desea ajustar el tamaño de fuente para llenar el contenido de UILabel, al menos debe proporcionar un texto.
Paulo Miguel Almeida
Esto tiene un inconveniente. Se divide en línea entre los caracteres, por lo que verá las palabras divididas en diferentes líneas. ¿Hay alguna manera de evitar esto?
Özgür
24

Línea única : hay dos formas, simplemente puede cambiar.

1- Pragmáticamente (Swift 3)

Solo agrega el siguiente código

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

2 - Uso del inspector de atributos 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)

ingrese la descripción de la imagen aquí

Devendra Singh
fuente
22

Es 2015. Tuve que buscar una publicación de blog que explicara cómo hacerlo para la última versión de iOS y XCode con Swift para que funcione con varias líneas.

  1. establezca "Autoshrink" en "Tamaño mínimo de fuente".
  2. establecer la fuente al tamaño de fuente deseable más grande (elegí 20)
  3. Cambie "Saltos de línea" de "Ajuste de línea" a "Truncar cola".

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

zavtra
fuente
¡Esto es realmente un salvavidas!
bhakti123
2
Súper genial ... Ese punto de cola truncado es lo más importante ... Coz en el caso de la distribución automática de ajuste de texto no siente la necesidad de disminuir el tamaño de la fuente, mientras que cuando se trunca la distribución automática de cola tiene que guardar el texto de la hoja y es entonces que cambia el tamaño de la fuente.
GKK
12

Versión rápida:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
Esqarrouth
fuente
Gracias .. Parece que aquí la secuencia también importa
Pramod
7

Aquí hay una extensión Swift para UILabel. Ejecuta un algoritmo de búsqueda binaria para cambiar el tamaño de la fuente en función del ancho y alto de los límites de la etiqueta. Probado para trabajar con iOS 9 y autolayout.

USO: ¿Dónde <label>está su UILabel predefinido que necesita cambiar el tamaño de la fuente?

<label>.fitFontForSize()

Por defecto, esta función busca dentro del rango de tamaños de fuente de 5pt y 300pt y establece la fuente para que se ajuste a su texto "perfectamente" dentro de los límites (con precisión dentro de 1.0pt). Puede definir los parámetros para que, por ejemplo, busque entre 1pt y el tamaño de fuente actual de la etiqueta con precisión dentro de 0.1pts de la siguiente manera:

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

Copie / pegue el siguiente código en su archivo

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 una de las layoutIfNeeded()llamadas se puede eliminar a su propia discreción

Avi Frankl
fuente
Ah, pero en realidad no funciona con autolayout; los "sizeToFit" no hacen nada en ese caso.
Fattie
4

No es un poco sofisticado, pero esto debería funcionar, por ejemplo, digamos que desea limitar su uilabel a 120x120, con un tamaño de fuente máximo de 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
fuente
Esto parece bastante ineficiente: debe permitir que el UILabel se ajuste dinámicamente para ajustarse en algún espacio disponible proporcionado. Si ejecuta esto para algo así como el cálculo de la fuente del título de una celda de vista de tabla, obtendrá problemas importantes de retraso. El enfoque puede funcionar, pero definitivamente no es recomendable.
Zorayr
Vota por ser la única persona que realmente responde la pregunta.
Jai
2

Simplemente envíe el mensaje sizeToFit a UITextView. Ajustará su propia altura para ajustarse a su texto. No cambiará su propio ancho u origen.

[textViewA1 sizeToFit];
codercat
fuente
¿Qué sucede cuando el tamaño que se ajusta al texto es demasiado grande para el espacio del contenedor? Por ejemplo, supongamos que tiene 100 puntos disponibles para ajustarse a la vista de texto, después de llamar a sizeToFitsu se textViewA1convierte en 200 puntos que termina recortándose.
Zorayr
0

Versión 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
fuente
0

Esta solución funciona para líneas múltiples:

Después de seguir varios artículos y de requerir una función que escale automáticamente el texto y ajuste el recuento de líneas para que se ajuste mejor al tamaño de etiqueta dado, escribí una función yo mismo. (es decir, una cadena corta encajaría bien en una línea y usaría una gran cantidad del marco de la etiqueta, mientras que una fuerte larga se dividiría automáticamente en 2 o 3 líneas y ajustaría el tamaño en consecuencia)

Siéntase libre de reutilizarlo y ajustarlo según sea necesario. Asegúrese de llamarlo después de que viewDidLayoutSubviewshaya terminado para que se haya configurado el marco de etiqueta inicial.

+ (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
fuente
0

Aquí está el código de relleno de una subclase de UILabel que implementa un cambio de tamaño de fuente animado:

@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
fuente