Modifica dinamica della dimensione del carattere di UILabel


188

Al momento ho 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];

Per tutta la vita della mia applicazione iOS, factLabelottiene un sacco di valori diversi. Alcuni con più frasi, altri con solo 5 o 6 parole.

Come posso impostare in UILabelmodo che la dimensione del carattere cambi in modo che il testo si adatti sempre ai limiti che ho definito?


2
Per il 2016, credo davvero che l'unica buona soluzione sia quella di utilizzare l'approccio "usa il ripensamento automatico". Rendi la casella UILabel la dimensione effettiva che desideri, fai riempire il carattere alla UILabel, seleziona il ripensamento automatico, imposta una dimensione del carattere enorme (300) e assicurati di provare sui simulatori più piccoli / più grandi. (Quindi, 4s / PadPro al momento). Spiegazione completa: stackoverflow.com/a/35154493/294884 Questa è l'unica vera soluzione oggi.
Fattie,

Risposte:


370

Linea singola:

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

Il codice sopra regolerà la dimensione del carattere del testo fino a (ad esempio) 8cercando di adattare il testo all'interno dell'etichetta. numberOfLines = 1è obbligatorio.

Più linee:

Perché numberOfLines > 1esiste un metodo per capire la dimensione del testo finale attraverso sizeWithFont di NSString: ... Metodi di aggiunta UIKit , ad esempio:

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

Dopodiché puoi semplicemente ridimensionare l'etichetta usando risultante lLabelSize, ad esempio (supponendo che cambierai solo l'altezza dell'etichetta):

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

iOS6

Linea singola:

A partire da iOS6, minimumFontSizeè stato deprecato. La linea

factLabel.minimumFontSize = 8.;

può essere modificato in:

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

iOS 7

Più linee:

A partire da iOS7, sizeWithFontdiventa obsoleto. Il caso multilinea è ridotto 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

ma questo mette il testo tutto su una riga. e se cambio factLabel.numberOfLines, la dimensione del carattere non cambia dinamicamente.
CodeGuy

@ reising1: hai ragione. Questo è solo il modo di creare un framework per fare il lavoro di ridimensionamento per te.
Martin Babacaev,

quindi la risposta alla mia domanda è che non c'è modo di farlo usando il framework fornito?
CodeGuy

1
@ reising1: In questo caso puoi anche usare il metodo di aggiunta di NSString UIKit: sizeWithFont:constrainedToSize:lineBreakMode:Ma in questo modo è un po 'difficile
Martin Babacaev,

6
È obsoleto da iOS6. Sostituiscilo conmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert,

72

minimumFontSizeè stato deprecato con iOS 6. È possibile utilizzare minimumScaleFactor.

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

Questo si occuperà della dimensione del carattere in base alla larghezza dell'etichetta e del testo.


Di solito uso 0,8, perché anche lo 0,7 tende a sembrare troppo piccolo. Ovviamente un po 'di testo potrebbe non adattarsi al fattore di scala minimo 0,8, si tratta di decidere cosa sembra migliore e dove le cose diventano illeggibili. OTOH le mie app possono essere ruotate, il che aiuta molto.
gnasher729,

adjustsFontSizeToFitWidthriduce il testo solo se non si adatta al contenitore
user25

24

Sulla base della risposta di @Eyal Ben Dov potresti voler creare una categoria per renderla flessibile da usare all'interno di un'altra tua app.

Obs .: Ho aggiornato il suo codice per renderlo compatibile con iOS 7

-File principale

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-File di implementazione

#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

-usage

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Saluti


non funziona per me. Il contenuto del mio UILabel è ora interrotto.
Adrian,

1
Se non funziona per te, probabilmente è perché il frame dell'etichetta non è ancora impostato. Prova a impostare il frame prima di chiamare questo (o chiama setNeedsLayout/ layoutIfNeededse stai usando AutoLayout).
bmueller

Dà il seguente arresto anomalo "'NSInvalidArgumentException', motivo: 'NSConcreteAttributedString initWithString :: valore nullo'"
Mohamed Saleh

Significa che la tua NSString non può essere nulla. Suppongo che se desideri regolare la dimensione del carattere per riempire il contenuto di UILabel devi almeno fornire un testo.
Paulo Miguel Almeida,

Questo ha uno svantaggio. Si interrompe la linea tra i caratteri, quindi vedi le parole divise in linee diverse. C'è un modo per aggirare questo?
Özgür,

24

Linea singola : ci sono due modi, puoi semplicemente cambiare.

1- Pragmaticamente (Swift 3)

Aggiungi il seguente codice

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

2 - Utilizzo dell'ispettore 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)

inserisci qui la descrizione dell'immagine


22

È il 2015. Ho dovuto trovare un post sul blog che spiegasse come farlo per l'ultima versione di iOS e XCode con Swift in modo che funzionasse con più linee.

  1. impostare "Rimpicciolimento automatico" su "Dimensione minima del carattere".
  2. imposta il carattere sulla dimensione massima desiderata (ho scelto 20)
  3. Modifica "Interruzioni di riga" da "A capo automatico" in "Troncare coda".

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


Questo è davvero un salvavita !!
bhakti123,

2
Super cool .. Quel punto di coda troncato è il più importante .. Perché in caso di avvolgimento di parole il layout automatico non sente l'impulso di ridurre la dimensione del carattere, mentre quando è troncato il coda automatica è necessario salvare il testo dal pannello è allora che ridimensiona il carattere.
GKK

12

Versione rapida:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Grazie .. Sembra che anche la sequenza maturi
Pramod

7

Ecco un'estensione Swift per UILabel. Esegue un algoritmo di ricerca binaria per ridimensionare il carattere in base alla larghezza e all'altezza dei limiti dell'etichetta. Testato per funzionare con iOS 9 e autolayout.

UTILIZZO: dove si <label>trova il tuo UILabel predefinito che richiede il ridimensionamento dei caratteri

<label>.fitFontForSize()

Per impostazione predefinita, questa funzione cerca nell'intervallo di dimensioni dei caratteri 5pt e 300pt e imposta il carattere in modo che si adatti al testo "perfettamente" entro i limiti (preciso entro 1,0pt). È possibile definire i parametri in modo che, ad esempio, le ricerche tra 1pt e la dimensione del carattere corrente dell'etichetta accuratamente all'interno 0.1pts nel modo seguente:

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

Copia / incolla il seguente codice nel tuo file

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: ciascuna delle layoutIfNeeded()chiamate può essere rimossa a propria discrezione


Ah - ma in realtà non funziona con il layout automatico; le "sizeToFit" non fanno nulla in quel caso.
Fattie,

4

Non è un po 'sofisticato ma dovrebbe funzionare, ad esempio supponiamo che tu voglia limitare il tuo uilabel a 120x120, con una dimensione massima del carattere di 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;
        }
    }

Questo sembra piuttosto inefficiente: dovresti lasciare che UILabel si alzi dinamicamente per adattarsi allo spazio disponibile disponibile. Se lo esegui per qualcosa come il calcolo del carattere del titolo di una cella di visualizzazione tabella, otterrai importanti problemi di ritardo. L'approccio potrebbe funzionare, ma sicuramente non raccomandato.
Zorayr,

Voto positivo per essere l'unica persona a rispondere effettivamente alla domanda.
Jai

2

Invia semplicemente il messaggio sizeToFit a UITextView. Adatterà la propria altezza per adattarsi al suo testo. Non cambierà la propria larghezza o origine.

[textViewA1 sizeToFit];

Cosa succede quando la dimensione adatta al testo è troppo grande per lo spazio del contenitore? Ad esempio, supponiamo che tu abbia 100 punti disponibili per adattarsi alla visualizzazione del testo, dopo aver chiamato il sizeToFittuo textViewA1diventa 200 punti che finisce per essere ritagliato.
Zorayr,

0

Versione 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

Questa soluzione funziona per multiline:

Dopo aver seguito diversi articoli e aver richiesto una funzione che ridimensionasse automaticamente il testo e regolasse il conteggio delle righe per adattarsi al meglio alle dimensioni dell'etichetta, ho scritto una funzione da solo. (ad es. una stringa corta si adatterebbe perfettamente su una riga e utilizzerebbe una grande quantità del frame dell'etichetta, mentre una stringa lunga si dividerebbe automaticamente su 2 o 3 righe e regolerebbe le dimensioni di conseguenza)

Sentiti libero di riutilizzarlo e modificare come richiesto. Assicurati di chiamarlo dopo viewDidLayoutSubviewsaver terminato in modo che il frame dell'etichetta iniziale sia stato impostato.

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

Ecco il codice di riempimento di una sottoclasse UILabel che implementa la modifica della dimensione del carattere animato:

@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];
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.