Dynamisch wechselnde Schriftgröße von UILabel

188

Ich habe derzeit eine 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];

Während der gesamten Lebensdauer meiner iOS-Anwendung werden factLabelverschiedene Werte angezeigt. Einige mit mehreren Sätzen, andere mit nur 5 oder 6 Wörtern.

Wie kann ich das UILabelso einrichten, dass sich die Schriftgröße ändert, sodass der Text immer in die von mir definierten Grenzen passt?

CodeGuy
quelle
2
Für 2016 glaube ich wirklich, dass die einzig gute Lösung darin besteht, den Ansatz "Autoshrinking verwenden" zu verwenden. Stellen Sie das UILabel-Feld auf die gewünschte tatsächliche Größe ein, lassen Sie die Schriftart das UILabel ausfüllen, wählen Sie Autoshrink aus, legen Sie eine große Schriftgröße (300) fest und testen Sie auf den kleinsten / größten Simulatoren. (Also derzeit 4s / PadPro.) Vollständige Erklärung: stackoverflow.com/a/35154493/294884 Dies ist heute die einzige echte Lösung.
Fattie

Antworten:

370

Einzelne Zeile:

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

Mit dem obigen Code wird die Schriftgröße Ihres Textes so angepasst, dass (zum Beispiel) 8versucht wird, Ihren Text in die Beschriftung einzufügen. numberOfLines = 1ist obligatorisch.

Mehrere Zeilen:

Denn numberOfLines > 1es gibt eine Methode, um die Größe des endgültigen Textes mithilfe von NSStrings sizeWithFont: ... UIKit-Additionsmethoden zu ermitteln, zum Beispiel:

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

Danach können Sie einfach die Größe Ihres Etiketts ändern lLabelSize, indem Sie beispielsweise das Ergebnis verwenden (vorausgesetzt, Sie ändern nur die Höhe des Etiketts):

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

iOS6

Einzelne Zeile:

Ab iOS6 minimumFontSizeist veraltet. Die Linie

factLabel.minimumFontSize = 8.;

kann geändert werden zu:

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

iOS7

Mehrere Zeilen:

Ab iOS7 sizeWithFontwird es veraltet. Der mehrzeilige Fall reduziert sich auf:

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
quelle
aber dies setzt den Text alle in eine Zeile. und wenn ich die factLabel.numberOfLines ändere, ändert sich die Schriftgröße nicht dynamisch.
CodeGuy
@ reising1: du hast recht. Auf diese Weise können Sie ein Framework erstellen, mit dem Sie die Größe ändern können.
Martin Babacaev
Die Antwort auf meine Frage lautet also, dass es keine Möglichkeit gibt, dies mit dem bereitgestellten Framework zu tun.
CodeGuy
1
@ reising1: In diesem Fall können Sie auch die Methode von NSString UIKit Addition verwenden: sizeWithFont:constrainedToSize:lineBreakMode:Aber dieser Weg ist ein bisschen schwierig
Martin Babacaev
6
Es ist seit iOS6 veraltet. Ersetzen Sie es durchmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert
72

minimumFontSizewurde mit iOS 6 veraltet. Sie können verwenden minimumScaleFactor.

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

Dadurch wird Ihre Schriftgröße entsprechend der Breite des Etiketts und des Texts berücksichtigt.

Amit Singh
quelle
Normalerweise benutze ich 0,8, weil sogar 0,7 zu ​​klein aussieht. Natürlich passt ein Teil des Textes möglicherweise nicht mit dem minimalen Skalierungsfaktor 0,8. Es ist eine Frage der Entscheidung, was besser aussieht und wo Dinge unlesbar werden. OTOH meine Apps können gedreht werden, was sehr hilfreich ist.
Gnasher729
adjustsFontSizeToFitWidthreduziert nur Text, wenn er nicht in den Container passt
user25
24

Basierend auf der Antwort von @Eyal Ben Dov möchten Sie möglicherweise eine Kategorie erstellen, um die Verwendung in anderen Apps von Ihnen flexibel zu gestalten.

Hinweis: Ich habe seinen Code aktualisiert, um ihn mit iOS 7 kompatibel zu machen

-Header-Datei

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Implementierungsdatei

#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

-Verwendung

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Prost

Paulo Miguel Almeida
quelle
es funktioniert nicht für mich. Der Inhalt meines UILabels ist jetzt abgeschnitten.
Adrian
1
Wenn es bei Ihnen nicht funktioniert, liegt es wahrscheinlich daran, dass der Rahmen des Etiketts noch nicht festgelegt ist. Versuchen Sie, den Frame vor dem Aufrufen festzulegen (oder rufen Sie setNeedsLayout/ auf, layoutIfNeededwenn Sie AutoLayout verwenden).
bmueller
Es gibt den folgenden Absturz "'NSInvalidArgumentException', Grund: 'NSConcreteAttributedString initWithString :: nil value'"
Mohamed Saleh
Dies bedeutet, dass Ihr NSString nicht Null sein kann. Ich gehe davon aus, dass Sie mindestens einen Text angeben müssen, wenn Sie die Schriftgröße anpassen möchten, um den Inhalt von UILabel zu füllen.
Paulo Miguel Almeida
Dies hat einen Nachteil. Die Zeilenumbrüche zwischen den Zeichen werden angezeigt, sodass die Wörter in verschiedene Zeilen unterteilt sind. Gibt es eine Möglichkeit, dies zu umgehen?
Özgür
23

Einzeilig - Es gibt zwei Möglichkeiten, die Sie einfach ändern können.

1- Pragmatisch (Swift 3)

Fügen Sie einfach den folgenden Code hinzu

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

2 - Verwenden des UILabel-Attributinspektors

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)

Geben Sie hier die Bildbeschreibung ein

Devendra Singh
quelle
22

Es ist 2015. Ich musste einen Blog-Beitrag suchen, in dem erklärt wurde, wie dies für die neueste Version von iOS und XCode mit Swift gemacht wird, damit es mit mehreren Zeilen funktioniert.

  1. Stellen Sie "Autoshrink" auf "Minimum font size".
  2. Stellen Sie die Schriftart auf die größte gewünschte Schriftgröße ein (ich habe 20 gewählt).
  3. Ändern Sie "Zeilenumbrüche" von "Zeilenumbruch" in "Schwanz abschneiden".

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

zavtra
quelle
Das ist wirklich ein Lebensretter !!
Bhakti123
2
Super cool. Dieser abgeschnittene Endpunkt ist der wichtigste. Coz im Falle eines automatischen Layouts für Zeilenumbrüche verspürt nicht den Drang, die Schriftgröße zu verringern, wohingegen bei einem abgeschnittenen End-Autolayout der Text vom Blade und davon gespeichert werden muss ist dann, dass es die Größe der Schriftart ändert.
GKK
12

Schnelle Version:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
Esqarrouth
quelle
Danke .. Scheint wie hier Sequenz auch wichtig
Pramod
7

Hier ist eine Swift-Erweiterung für UILabel. Es wird ein binärer Suchalgorithmus ausgeführt, um die Schriftgröße basierend auf der Breite und Höhe der Beschriftungsgrenzen zu ändern. Getestet für iOS 9 und Autolayout.

VERWENDUNG: Wo <label>befindet sich Ihr vordefiniertes UILabel, dessen Schriftgröße geändert werden muss?

<label>.fitFontForSize()

Standardmäßig sucht diese Funktion im Bereich von 5pt- und 300pt-Schriftgrößen und legt die Schriftart so fest, dass ihr Text "perfekt" innerhalb der Grenzen liegt (genau innerhalb von 1,0pt). Sie können die Parameter so definieren, dass beispielsweise zwischen 1 Punkt und der aktuellen Schriftgröße des Etiketts innerhalb von 0,1 Punkten auf folgende Weise genau gesucht wird:

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

Kopieren Sie den folgenden Code in Ihre Datei

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
    }

}

HINWEIS: Jeder layoutIfNeeded()Anruf kann nach eigenem Ermessen entfernt werden

Avi Frankl
quelle
Ah - aber mit Autolayout funktioniert es nicht wirklich. Die "sizeToFit" machen in diesem Fall nichts.
Fattie
4

Es ist ein bisschen nicht raffiniert, aber das sollte funktionieren. Nehmen wir zum Beispiel an, Sie möchten Ihr Uilabel auf 120 x 120 mit einer maximalen Schriftgröße von 28 begrenzen:

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
quelle
Dies scheint ziemlich ineffizient zu sein - Sie sollten das UILabel dynamisch anheben lassen, damit es in den bereitgestellten verfügbaren Platz passt. Wenn Sie dies für eine Berechnung der Titelschriftart einer Tabellenansichtszelle ausführen, treten erhebliche Verzögerungsprobleme auf. Der Ansatz mag funktionieren, wird aber definitiv nicht empfohlen.
Zorayr
Up-Voting als einzige Person, die die Frage tatsächlich beantwortet.
Jai
2

Senden Sie einfach die sizeToFit-Nachricht an UITextView. Es passt seine eigene Höhe an den Text an. Die eigene Breite oder Herkunft wird nicht geändert.

[textViewA1 sizeToFit];
Codercat
quelle
Was passiert, wenn die Größe, die zum Text passt, zu groß für den Platz des Containers ist? Nehmen wir zum Beispiel an, Sie haben 100 Punkte zur Verfügung, um in die Textansicht zu passen, nachdem Sie sizeToFitIhre textViewA1200 Punkte aufgerufen haben, was dazu führt, dass sie abgeschnitten werden.
Zorayr
0

Swift 2.0 Version:

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
quelle
0

Diese Lösung funktioniert für mehrzeilige:

Nachdem ich mehreren Artikeln gefolgt war und eine Funktion benötigt hatte, die den Text automatisch skaliert und die Zeilenanzahl so anpasst, dass sie am besten zur angegebenen Etikettengröße passt, habe ich selbst eine Funktion geschrieben. (dh eine kurze Zeichenfolge würde gut in eine Zeile passen und einen großen Teil des Etikettenrahmens verwenden, während eine lange starke Zeichenfolge automatisch in 2 oder 3 Zeilen aufgeteilt und die Größe entsprechend angepasst würde)

Fühlen Sie sich frei, es wiederzuverwenden und nach Bedarf zu optimieren. Stellen Sie sicher, dass Sie es nach viewDidLayoutSubviewsAbschluss aufrufen, damit der ursprüngliche Beschriftungsrahmen festgelegt wurde.

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

Hier ist der Füllcode einer UILabel-Unterklasse, die eine animierte Änderung der Schriftgröße implementiert:

@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];
}
Videoliste
quelle