Изменение размера UILabel для соответствия Word Wrap - PullRequest
6 голосов
/ 19 августа 2010

Это часть приложения для iPhone, но в целом оно должно применяться к Какао, написанному на objC.

У меня есть UILabel, содержащая различное количество текста (от одного символа до нескольких предложений).Текст всегда должен отображаться как можно большим шрифтом, который соответствует всему тексту в UILabel.Максимальное количество строк равно 4, а режим разрыва строки установлен на перенос слов.

Поскольку используется несколько строк, AdjusttsFontSizeToFitWidth не будет работать для изменения размера текста.

Таким образом, яЯ использую цикл, чтобы определить максимально возможный размер шрифта для каждой строки, например:

    //Set the text  
    self.textLabel.text = text;
    //Largest size used  
    NSInteger fsize = 200;  textLabel.font = [UIFont
    fontWithName:@"Verdana-Bold"
    size:fsize];

    //Calculate size of the rendered string with the current parameters
    float height = [text sizeWithFont:textLabel.font
        constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
        lineBreakMode:UILineBreakModeWordWrap].height;

    //Reduce font size by 5 while too large, break if no height (empty string)
    while (height > textLabel.bounds.size.height and height != 0) {   
        fsize -= 5;  
        textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];   
        height = [text sizeWithFont:textLabel.font 
            constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
            lineBreakMode:UILineBreakModeWordWrap].height;
    };

Этот подход работает в большинстве случаев хорошо.Исключение составляют длинные слова.Давайте возьмем строку @ "Опыт Foo."В качестве примера.Слово «опыт», будучи намного длиннее других, будет разбито пополам без правильной переноса слов, а строка разделена на 4 строки.Я ищу способ еще больше уменьшить размер, чтобы каждое отдельное слово помещалось в одну строку.

Пример:

-old-

Размер шрифта: 60 ​​

The
Exper
ience
foo

должно быть

-new-

Размер шрифта: 30

The
Experience
foo

Естьвероятно, это простой способ сделать это, но я бью стену.

Ответы [ 5 ]

13 голосов
/ 25 августа 2010

Вот самый изящный (хотя и несколько хакерский) способ, который я нашел, чтобы сделать эту работу:

  1. Разделить строку на слова
  2. Рассчитать ширину каждого слова, используя текущий размер шрифта
  3. Уменьшать размер строки, пока каждое слово не помещается в одну строку

Потребление ресурсов достаточно низкое для того, чтобы это работало даже в UITableViews, полном строк, отредактированных таким образом.

Вот новый код:

//Set the text  
self.textLabel.text = text;
//Largest size used  
NSInteger fsize = 200;  textLabel.font = [UIFont fontWithName:@"Verdana-Bold"
                                                         size:fsize];

//Calculate size of the rendered string with the current parameters
float height = 
      [text sizeWithFont:textLabel.font
       constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
           lineBreakMode:UILineBreakModeWordWrap].height;

//Reduce font size by 5 while too large, break if no height (empty string)
while (height > textLabel.bounds.size.height and height != 0) {   
    fsize -= 5;  
    textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];   
    height = [text sizeWithFont:textLabel.font 
              constrainedToSize:CGSizeMake(textLabel.bounds.size.width,99999) 
                  lineBreakMode:UILineBreakModeWordWrap].height;
};

// Loop through words in string and resize to fit
for (NSString *word in [text componentsSeparatedByString:@" "]) {
    float width = [word sizeWithFont:textLabel.font].width;
    while (width > textLabel.bounds.size.width and width != 0) {
        fsize -= 3;
        textLabel.font = [UIFont fontWithName:@"Verdana-Bold" size:fsize];
        width = [word sizeWithFont:textLabel.font].width;

    }
}
4 голосов
/ 11 апреля 2013

Вот моя версия ответа 0x90 в категории:

@implementation UILabel (MultilineAutosize)

- (void)adjustFontSizeToFit
{
    //Largest size used
    NSInteger fsize = self.font.pointSize;

    //Calculate size of the rendered string with the current parameters
    float height = [self.text sizeWithFont:self.font
                         constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT)
                             lineBreakMode:NSLineBreakByWordWrapping].height;

    //Reduce font size by 5 while too large, break if no height (empty string)
    while (height > self.bounds.size.height && height > 0) {
        fsize -= 5;
        self.font = [self.font fontWithSize:fsize];
        height = [self.text sizeWithFont:self.font
                       constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT)
                           lineBreakMode:NSLineBreakByWordWrapping].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
        float width = [word sizeWithFont:self.font].width;
        while (width > self.bounds.size.width && width > 0) {
            fsize -= 3;
            self.font = [self.font fontWithSize:fsize];
            width = [word sizeWithFont:self.font].width;
        }
    }
}

@end
3 голосов
/ 13 июля 2011

Вы можете использовать код выше в категории для UILabel

UILabel + AdjustFontSize.h

@interface UILabel (UILabel_AdjustFontSize)

- (void) adjustsFontSizeToFitWidthWithMultipleLinesFromFontWithName:(NSString*)fontName size:(NSInteger)fsize andDescreasingFontBy:(NSInteger)dSize;

@end

UILabel + AdjustFontSize.m

@implementation UILabel (UILabel_AdjustFontSize)

- (void) adjustsFontSizeToFitWidthWithMultipleLinesFromFontWithName:(NSString*)fontName size:(NSInteger)fsize andDescreasingFontBy:(NSInteger)dSize{

    //Largest size used  
    self.font = [UIFont fontWithName:fontName size:fsize];

    //Calculate size of the rendered string with the current parameters
    float height = [self.text sizeWithFont:self.font
                    constrainedToSize:CGSizeMake(self.bounds.size.width,99999) 
                        lineBreakMode:UILineBreakModeWordWrap].height;

    //Reduce font size by dSize while too large, break if no height (empty string)
    while (height > self.bounds.size.height && height != 0) {   
        fsize -= dSize;
        self.font = [UIFont fontWithName:fontName size:fsize];   
        height = [self.text sizeWithFont:self.font 
                  constrainedToSize:CGSizeMake(self.bounds.size.width,99999) 
                      lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
        float width = [word sizeWithFont:self.font].width;
        while (width > self.bounds.size.width && width != 0) {
            fsize -= dSize;
            self.font = [UIFont fontWithName:fontName size:fsize];
            width = [word sizeWithFont:self.font].width;            
        }
    }
}

@end
0 голосов
/ 20 июня 2019

Расширение UILabel в Swift 4 на основе ответа 0x90:

func adjustFontSizeToFit() {
    guard var font = self.font, let text = self.text else { return }
    let size = self.frame.size
    var maxSize = font.pointSize
    while maxSize >= self.minimumScaleFactor * self.font.pointSize {
        font = font.withSize(maxSize)
        let constraintSize = CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude)
        let textRect = (text as NSString).boundingRect(with: constraintSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : font], context: nil)
        let labelSize = textRect.size
        if labelSize.height <= size.height {
            self.font = font
            self.setNeedsLayout()
            break
        }
        maxSize -= 1
    }
    self.font = font;
    self.setNeedsLayout()
}
0 голосов
/ 12 сентября 2018

Это отличный вопрос, и вы могли бы подумать, что использование максимально возможного размера шрифта без разбивки слов будет частью встроенной функциональности UIKit или связанной с ней инфраструктуры. Вот хороший наглядный пример вопроса:

Font resizing animation

Как описано другими, хитрость заключается в том, чтобы выполнять поиск по размеру для отдельных слов, а также для всего текста в целом. Это связано с тем, что при указании ширины для рисования отдельных слов методы определения размера разбивают слова на части, поскольку у них нет другого выбора - вы просите их нарисовать «неразрывную» строку с определенным размером шрифта в регионе это просто не подходит.

В основе моего рабочего решения я использую следующую функцию двоичного поиска:

func binarySearch(string: NSAttributedString, minFontSize: CGFloat, maxFontSize: CGFloat, maxSize: CGSize, options: NSStringDrawingOptions) -> CGFloat {
    let avgSize = roundedFontSize((minFontSize + maxFontSize) / 2)
    if avgSize == minFontSize || avgSize == maxFontSize { return minFontSize }
    let singleLine = !options.contains(.usesLineFragmentOrigin)
    let canvasSize = CGSize(width: singleLine ? .greatestFiniteMagnitude : maxSize.width, height: .greatestFiniteMagnitude)
    if maxSize.contains(string.withFontSize(avgSize).boundingRect(with: canvasSize, options: options, context: nil).size) {
      return binarySearch(string: string, minFontSize:avgSize, maxFontSize:maxFontSize, maxSize: maxSize, options: options)
    } else {
      return binarySearch(string: string, minFontSize:minFontSize, maxFontSize:avgSize, maxSize: maxSize, options: options)
    }
  }

Одного этого недостаточно. Вы должны использовать его, чтобы сначала найти максимальный размер, который будет соответствовать самому длинному слову в границах. Как только вы это сделаете, продолжайте поиск меньшего размера, пока весь текст не уместится. Таким образом, ни одно слово никогда не будет разбито. Есть некоторые дополнительные соображения, которые немного более сложны, включая определение того, какое слово на самом деле длиннее (есть некоторые ошибки!), А также производительность кэширования шрифтов iOS.

Если вам нужно только показать текст на экране простым способом, я разработал надежную реализацию в Swift, которую я также использую в производственном приложении. Это подкласс UIView с эффективным автоматическим масштабированием шрифта для любого вводимого текста, включая несколько строк. Чтобы использовать его, вы просто должны сделать что-то вроде:

let view = AKTextView()
// Use a simple or fancy NSAttributedString
view.attributedText = .init(string: "Some text here")
// Add to the view hierarchy somewhere

Вот и все! Вы можете найти полный исходный код здесь: https://github.com/FlickType/AccessibilityKit

Надеюсь, это поможет!

...