Dynamicznie zmieniający się rozmiar czcionki UILabel


188

Obecnie mam 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];

Przez całe życie mojej aplikacji na iOS factLabeldostaje mnóstwo różnych wartości. Niektóre z wieloma zdaniami, inne z zaledwie 5 lub 6 słowami.

Jak mogę ustawić UILabeltak, aby rozmiar czcionki zmieniał się, aby tekst zawsze mieścił się w zdefiniowanych przeze mnie granicach?


2
Naprawdę uważam, że w 2016 r. Jedynym dobrym rozwiązaniem jest zastosowanie metody „użyj automatycznego ograniczania prędkości”. Ustaw pole UILabel w żądanym rozmiarze, spraw, aby czcionka wypełniała UILabel, wybierz autoshrink, ustaw tytułowy ogromny rozmiar czcionki (300) i koniecznie przetestuj na najmniejszych / największych symulatorach. (Tak więc obecnie 4s / PadPro.) Pełne wyjaśnienie: stackoverflow.com/a/35154493/294884 To jedyne prawdziwe rozwiązanie na dziś.
— Fattie

Odpowiedzi:


370

Pojedyncza linia:

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

Powyższy kod dostosuje rozmiar czcionki tekstu do (na przykład) 8próby dopasowania tekstu do etykiety. numberOfLines = 1jest obowiązkowe.

Wiele linii:

Dla numberOfLines > 1istnieje metoda, aby dowiedzieć się rozmiar tekstu końcowego przez NSString za sizeWithFont ... addycyjnych UIKit metody, na przykład:

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

Następnie możesz po prostu zmienić rozmiar etykiety przy użyciu wynikowego lLabelSize, na przykład (zakładając, że zmienisz tylko wysokość etykiety):

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

iOS6

Pojedyncza linia:

Począwszy od iOS6, minimumFontSizebył przestarzały. Linia

factLabel.minimumFontSize = 8.;

można zmienić na:

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

iOS7

Wiele linii:

Począwszy od iOS7, sizeWithFontstaje się przestarzały. Obudowa multilinii została zredukowana do:

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

ale to umieszcza tekst w jednym wierszu. a jeśli zmienię factLabel.numberOfLines, wówczas rozmiar czcionki nie zmienia się dynamicznie.
— CodeGuy

@ reising1: masz rację. Oto jak stworzyć ramy do zmiany rozmiaru pracy.
— Martin Babacaev,

więc odpowiedź na moje pytanie brzmi: nie ma sposobu, aby to zrobić przy użyciu dostarczonych ram?
— CodeGuy

1
@ reising1: W tym przypadku możesz także użyć metody dodawania NSString UIKit: sizeWithFont:constrainedToSize:lineBreakMode:Ale ten sposób jest trochę trudny
— Martin Babacaev

6
Jest przestarzałe od iOS6. Zamień namyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
— Norbert,

72

minimumFontSizezostał wycofany z iOS 6. Możesz używać minimumScaleFactor.

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

Spowoduje to dostosowanie rozmiaru czcionki do szerokości etykiety i tekstu.


Zwykle używam 0,8, ponieważ nawet 0,7 wydaje się zbyt małe. Oczywiście, niektóre teksty mogą nie pasować do minimalnego współczynnika skali 0,8, należy zdecydować, co wygląda lepiej, a gdzie coś staje się nieczytelne. OTOH moje aplikacje można obracać, co bardzo pomaga.
— gnasher729

adjustsFontSizeToFitWidthzmniejsza tekst tylko wtedy, gdy nie mieści się w kontenerze
— 25

24

W oparciu o odpowiedź @Eyal Ben Dov możesz chcieć utworzyć kategorię, aby uczynić ją elastyczną w innych aplikacjach.

Obs .: Zaktualizowałem jego kod, aby był zgodny z iOS 7

Plik nagłówka

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Implementation file

#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

-Stosowanie

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Twoje zdrowie


to nie działa dla mnie. Zawartość mojego UILabel jest teraz odcięta.
— Adrian

1
Jeśli to nie działa, to prawdopodobnie dlatego, że ramka etykiety nie jest jeszcze ustawiona. Spróbuj ustawić ramkę przed wywołaniem tego (lub zadzwoń setNeedsLayout/ layoutIfNeededjeśli korzystasz z AutoLayout).
— bmueller

Daje następującą awarię „” NSInvalidArgumentException ”, powód:„ NSConcreteAttributString initWithString :: zero wartość ””
— Mohamed Saleh

Oznacza to, że twój NSString nie może być zerowy. Zakładam, że jeśli chcesz dostosować rozmiar czcionki, aby wypełnić zawartość UILabel, musisz przynajmniej podać tekst.
— Paulo Miguel Almeida

Ma to wadę. Linia dzieli się między znakami, dzięki czemu słowa dzielą się na różne linie. Czy istnieje sposób na obejście tego?
— Özgür,

24

Pojedyncza linia - są dwa sposoby, które możesz po prostu zmienić.

1- Pragmatycznie (Swift 3)

Wystarczy dodać następujący kod

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

2 - Korzystanie z inspektora atrybutów 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)

wprowadź opis zdjęcia tutaj


22

Jest 2015. Musiałem znaleźć post na blogu, który wyjaśniłby, jak to zrobić w najnowszej wersji iOS i XCode z Swift, aby działał z wieloma liniami.

  1. ustaw „Autoshrink” na „Minimalny rozmiar czcionki”.
  2. ustaw czcionkę na największy pożądany rozmiar (wybrałem 20)
  3. Zmień „Podziały linii” z „Zawijanie słów” na „Obcinaj ogon”.

Źródło: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/


To naprawdę ratuje życie !!
— bhakti123

2
Super fajnie .. Ten obcięty punkt ogona jest najważniejszy .. Ponieważ w przypadku zawijania tekstu autoukład nie odczuwa potrzeby zmniejszenia rozmiaru czcionki, natomiast gdy jest ścięty, autoukład musi zapisać tekst z ostrza i to oznacza to, że zmienia rozmiar czcionki.
— GKK,

12

Wersja szybka:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Dzięki .. Wygląda na to, że sekwencja również ma znaczenie
— Pramod

7

Oto rozszerzenie Swift dla UILabel. Działa algorytm wyszukiwania binarnego, aby zmienić rozmiar czcionki na podstawie szerokości i wysokości granic etykiety. Testowany do pracy z iOS 9 i autolayout.

SPOSÓB UŻYCIA: Gdzie <label>jest twój wstępnie zdefiniowany UILabel, który wymaga zmiany rozmiaru czcionki

<label>.fitFontForSize()

Domyślnie ta funkcja wyszukuje w zakresie rozmiarów czcionek 5pt i 300pt i ustawia czcionkę tak, aby „idealnie” mieściła się w tekście (w granicach 1,0pt). Można zdefiniować parametry, aby na przykład wyszukiwało między 1pt a bieżącym rozmiarem czcionki z dokładnością do 0,1 punktu w następujący sposób:

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

Skopiuj / Wklej następujący kod do swojego pliku

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
    }

}

UWAGA: Każde z layoutIfNeeded()połączeń można usunąć według własnego uznania


Ach - ale tak naprawdę to nie działa z autolayout; „sizeToFit” nic w tym przypadku nie robi.
— Fattie

4

To trochę nie jest wyrafinowane, ale to powinno działać, na przykład powiedzmy, że chcesz ograniczyć swój uilabel do 120x120, przy maksymalnym rozmiarze czcionki 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;
        }
    }

Wydaje się to raczej nieefektywne - należy pozwolić, aby UILabel dynamicznie sam się wyrównał, aby zmieścił się w pewnej dostępnej przestrzeni. Jeśli uruchomisz to dla czegoś takiego jak obliczanie czcionki tytułu komórki w widoku tabeli, wystąpią poważne problemy z opóźnieniem. Podejście to może działać, ale zdecydowanie nie jest zalecane.
— Zorayr

Up-głosowanie za bycie jedyną osobą, która faktycznie odpowiedziała na pytanie.
— Jai

2

Wystarczy wysłać wiadomość sizeToFit do UITextView. Dostosuje swoją wysokość, tak aby pasowała do tekstu. Nie zmieni własnej szerokości ani pochodzenia.

[textViewA1 sizeToFit];

Co się stanie, gdy rozmiar, który pasuje do tekstu, jest zbyt duży, aby zmieścić się w pojemniku? Na przykład, powiedzmy, że masz 100 możliwych punktów, aby pasowały do widoku tekstu, po wywołaniu sizeToFitlisty textViewA1staje się 200 punktów, które kończy się coraz przycięte.
— Zorayr

0

Wersja 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

To rozwiązanie działa w przypadku multilinii:

Po przeczytaniu kilku artykułów i wymaganiu funkcji, która automatycznie skalowałaby tekst i dostosowywała liczbę wierszy do najlepiej dopasowanego do podanego rozmiaru etykiety, sam napisałem funkcję. (tzn. krótki sznurek dobrze pasowałby do jednej linii i używałby dużej ilości ramki etykiety, podczas gdy długi silny automatycznie dzieliłby się na 2 lub 3 linie i odpowiednio dostosowywałby rozmiar)

Możesz go ponownie użyć i dostosować w razie potrzeby. Pamiętaj, aby wywołać go po viewDidLayoutSubviewszakończeniu, aby ustawić początkową ramkę etykiety.

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

Oto kod wypełnienia podklasy UILabel, która wprowadza animowaną zmianę rozmiaru czcionki:

@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];
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.