Динамічно змінюється розмір шрифту UILabel


188

На даний момент у мене є 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];

Протягом життя мого додатка iOS factLabelотримує купу різних значень. Деякі з кількома реченнями, інші - лише з 5 або 6 словами.

Як я можу налаштувати UILabelтак, щоб розмір шрифту змінювався так, щоб текст завжди вписувався у визначені нами межі?


2
На 2016 рік я дійсно вважаю, що єдиним хорошим рішенням є використання підходу «використовувати автошеркінг». Зробіть поле UILabel фактичним потрібним розміром, змусьте шрифт заповнити UILabel, виберіть автоматичне посилання, встановіть титульний величезний розмір шрифту (300) і обов’язково протестуйте на найменших / найбільших тренажерах. (Отже, 4s / PadPro зараз.) Повне пояснення: stackoverflow.com/a/35154493/294884 Це єдине реальне рішення на сьогодні.
Fattie

Відповіді:


370

Одина рядок:

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

Вищевказаний код буде коригувати розмір шрифту вашого тексту до (наприклад), 8намагаючись помістити текст у мітку. numberOfLines = 1є обов'язковим.

Кілька ліній:

Бо numberOfLines > 1існує спосіб визначити розмір остаточного тексту через розмір NSString sizeWithFont: ... Методи додавання UIKit , наприклад:

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

Після цього ви можете просто змінити розмір мітки, використовуючи lLabelSize, наприклад, результат (якщо припустити, що ви зміните лише висоту мітки):

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

iOS6

Одина рядок:

Починаючи з iOS6, minimumFontSizeзастаріло. Лінія

factLabel.minimumFontSize = 8.;

можна змінити на:

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

iOS7

Кілька ліній:

Починаючи з iOS7, sizeWithFontстає застарілим. Шматок рядків зводиться до:

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

але це ставить весь текст в один рядок. і якщо я зміню factLabel.numberOfLines, розмір шрифту не змінюється динамічно.
CodeGuy

@ reising1: ти маєш рацію. Це лише те, як створити рамки для того, щоб зробити зміни розміру для вас.
Мартін Бабакаєв

тож відповідь на моє запитання полягає в тому, що немає способу зробити це за допомогою наданої рамки?
CodeGuy

1
@ reising1: У цьому випадку ви також можете скористатися методом доповнення NIKtring UIKit: sizeWithFont:constrainedToSize:lineBreakMode:Але цей спосіб трохи складний
Martin Babacaev

6
Це застаріло з iOS6. Замініть його наmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Норберт

72

minimumFontSizeзастаріла за допомогою iOS 6. Можна використовувати minimumScaleFactor.

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

Це забезпечить розмір вашого шрифту відповідно до ширини мітки та тексту.


Зазвичай я використовую 0,8, тому що навіть 0,7 має тенденцію виглядати занадто мало. Звичайно, якийсь текст може не відповідати мінімальному коефіцієнту шкали 0,8, це вирішити питання про те, що виглядає краще, і де речі не можна прочитати. ОТО мої програми можна повертати, що дуже допомагає.
gnasher729

adjustsFontSizeToFitWidthзменшує текст, лише якщо він не вміщується в контейнер
user25

24

На основі відповіді @Eyal Бен Дова ви можете створити категорію, щоб зробити її гнучкою для використання в інших ваших додатках.

Відповідь: Я оновив його код, щоб зробити сумісним із iOS 7

-Файл-читач

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Файл виконання

#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

-Використання

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Ура


це не працює для мене. Вміст мого UILabel зараз відрізаний.
Адріан

1
Якщо це не працює для вас, можливо, тому, що рамка етикетки ще не встановлена. Спробуйте встановити кадр перед тим, як зателефонувати цьому (або зателефонувати setNeedsLayout/ layoutIfNeededякщо ви використовуєте функцію AutoLayout).
bmueller

Це дає наступний збій "" NSInvalidArgumentException ", причина:" NSConcreteAttributedString initWithString :: нульове значення ""
Мохамед Салех

Це означає, що ваш NSString не може бути нульовим. Я припускаю, що якщо ви хочете відрегулювати розмір шрифту для заповнення вмісту UILabel, вам потрібно принаймні надати текст.
Пауло Мігель Альмейда

Це має недолік. Він переривається між символами, тож ви бачите слова, розбиті на різні рядки. Чи є спосіб це обійти?
Özgür

24

Одина рядок - Є два способи, які ви можете просто змінити.

1- Прагматично (Швидкий 3)

Просто додайте наступний код

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

2 - Використання інспектора атрибутів 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)

введіть тут опис зображення


22

Це 2015 рік. Мені довелося знайти пост в блозі, який би пояснив, як це зробити для останньої версії iOS та XCode з Swift, щоб вона працювала з кількома рядками.

  1. встановіть "Автоматичне посилання" на "Мінімальний розмір шрифту".
  2. встановіть шрифт на найбільший бажаний розмір шрифту (я вибрав 20)
  3. Змініть "Перерви лінії" з "Обертання слів" на "Обрізати хвіст".

Джерело: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/


Це справді рятівник життя !!
bhakti123

2
Супер круто .. Ця точка обрізання хвоста є найважливішою. ​​Коз у випадку автоматичного розгортання слів не відчуває бажання зменшити розмір шрифту, тоді як при врізанні хвостової автоматичної розробки доводиться зберігати текст із леза та його це те, що він змінює розмір шрифту.
GKK

12

Швидка версія:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Дякую .. Здається, що тут також має місце послідовність
Pramod

7

Ось розширення Swift для UILabel. Він запускає алгоритм бінарного пошуку для зміни розміру шрифту на основі ширини та висоти меж метки. Тестовано для роботи з iOS 9 та автоматичним розкладом.

ВИКОРИСТАННЯ: Де <label>знаходиться ваш попередньо визначений UILabel, який потребує зміни розміру шрифту

<label>.fitFontForSize()

За замовчуванням ця функція здійснює пошук у межах розмірів шрифту 5pt та 300pt та встановлює шрифт "ідеально" в межах тексту (точно в межах 1,0pt). Ви можете визначити параметри так, щоб він, наприклад, здійснював пошук між 1pt та поточним розміром шрифту мітки в межах 0,1пт наступним чином:

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

Скопіюйте / вставте наступний код у свій файл

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
    }

}

ПРИМІТКА. Кожен з layoutIfNeeded()дзвінків можна видалити на власний розсуд


Ах - але це не дуже працює з автоматичним розкладом; "sizeToFit" в цьому випадку нічого не робить.
Fattie

4

Це трохи не витончено, але це має спрацювати, наприклад, скажімо, що ви хочете обмежити розмір шрифту uilabel до 120x120 з максимальним розміром шрифту 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;
        }
    }

Це здається досить неефективним - ви повинні дозволити динамічно висоті UILabel, щоб вона вмістилася в деякий доступний простір. Якщо запустити це для чогось на зразок обчислення шрифту заголовка клітинки подання таблиці, ви отримаєте основні відстаючі проблеми. Підхід може працювати, але напевно не рекомендується.
Зорайр

Голосуйте за те, що єдина людина, яка фактично відповіла на питання.
Джай

2

Просто надішліть повідомлення sizeToFit до UITextView. Він налаштує власну висоту, щоб просто відповідати її тексту. Це не змінить власної ширини чи походження.

[textViewA1 sizeToFit];

Що відбувається, коли розмір, який відповідає тексту, занадто великий для простору контейнера? Наприклад, скажімо, у вас є 100 балів, щоб відповідати перегляду тексту, після виклику sizeToFitваш textViewA1набір стає 200 балів, що закінчується обрізанням.
Зорайр

0

Версія 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

Це рішення працює для багаторядкових:

Дотримуючись кількох статей і вимагаючи функції, яка автоматично масштабує текст і коригує кількість рядків, щоб найкращим чином підходити до заданого розміру мітки, я сам написав функцію. .

Не соромтесь повторно використовувати його та налаштувати за потребою. Переконайтеся, що ви зателефонували після viewDidLayoutSubviewsзавершення, щоб було встановлено початковий кадр мітки.

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

Ось код заповнення підкласу UILabel, який реалізує зміну розміру анімованого шрифту:

@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];
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.