iOS UILabel автоматически сжимается, поэтому слово не усекается до двух строк

Я пытаюсь уменьшить UILabel, чтобы слова не усекались до следующей строки. Не просто обрезание текста в конце текстовой области.

Если у меня есть поле размером 50x100, и я хочу вставить что-то вроде «американский» в поле размером 25,0 pt, я в итоге получаю:

   50px
 -------
|Ameri- |
|can    |
|Beauty | 100px
|       |
 -------

Сокращение текста, кажется, ничего не делает в этой ситуации, поскольку он все еще помещается во фрейме UILabel. Это работает очень хорошо, когда текст очень длинный, как «Шоколадная фабрика Вилли Вонки», но я не хочу усечения слов.

Это идеальный результат в этом сценарии:

    50px
 --------
[American|
|Beauty  | 100px
|        |
|        |
|        |
 --------

Любые предложения будут супер оценены!

Изменить: РЕШЕНИЕ

Вот что я сделал благодаря предложению в ответе ниже. Это прекрасно работает!

- (CGFloat) calculateFromUILabel:(UILabel *)label
{
    NSString *stringToMeasure = label.text;
    NSLog(@"FontSizeMeasurement.calculateFromUILabel() %@", stringToMeasure);

    NSRange range = NSMakeRange(0, 1);
    NSAttributedString *attributedString = label.attributedText;
    NSDictionary *attributes = [attributedString attributesAtIndex:0 effectiveRange:&range];

    NSMutableCharacterSet *characterSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
    [characterSet addCharactersInString:@"-"];
    NSArray *words = [stringToMeasure componentsSeparatedByCharactersInSet:characterSet];

    CGSize maxSize = CGSizeZero;
    NSMutableAttributedString *maxWidthString = nil;
    for(int i = 0; i < words.count; i++) {
        NSString *word = words[i];
        CGSize wordSize = [word sizeWithAttributes:attributes];
        if(wordSize.width > maxSize.width) {
            maxSize = wordSize;
            maxWidthString = [[NSMutableAttributedString alloc] initWithString:word attributes:attributes];
        }
    }

    UIFont *font = [label.font copy];
    while(maxSize.width > self.maxWidth) {
        font = [font fontWithSize:(font.pointSize-0.1)];
        [maxWidthString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, maxWidthString.length)];
        maxSize = [maxWidthString size];
    }

    return font.pointSize;
}

person normmcgarry    schedule 16.04.2014    source источник


Ответы (4)


Я не могу придумать ничего, что было бы встроено напрямую. Поэтому я бы предложил:

Разделите строку на компоненты по [NSCharacterSet +whitespaceAndNewlineCharacterSet] и [NSString -componentsSeparatedByCharactersInSet:]. Я подумал о том, чтобы порекомендовать NSLinguisticTagger более высокого уровня для вывода целых слов, но это не позволяло бы использовать такие вещи, как слова с двоеточием в конце.

Из этих слов найдите типографически самое большое, используя дополнение UIKit NSString -sizeWithAttributes: (под iOS 7) или -sizeWithFont: (до 6 или ниже). Вы сделаете предположение, что самый крупный останется самым большим при уменьшении размера шрифта, что, я думаю, всегда будет верным, потому что Apple не использует агрессивные подсказки шрифтов.

Если это слово уже меньше ширины вашего представления, все готово. Просто покажи строку.

В противном случае используйте быстрый бинарный поиск, неоднократно запрашивая размер, пока не найдете меньший размер шрифта, который вам нужен, с любой точностью, которую вы считаете подходящей (0,1 балла звучит для меня разумно, но вы поняли). Затем покажите всю строку в этом размере.

person Tommy    schedule 16.04.2014
comment
Эй, я очень ценю этот ответ. Итак, вы рекомендуете найти самое большое слово само по себе и уменьшить его размер до тех пор, пока это самое большое слово не станет меньше ширины 50 пикселей? [NSString -sizeWithAttributes: скажет мне CGSize этого слова в этом размере? Это может сработать! Я попробую и отчитаюсь. - person normmcgarry; 17.04.2014
comment
Это сработало отлично! Я добавил код того, что я сделал, используя ваше предложение. Работал как шарм. - person normmcgarry; 18.04.2014

Просто добавить версию Swift 4 + Добавить защиту, чтобы сделать это только с параметром AdjustsFontSizeToFitWidth true, так как пользователь, использующий false, не захочет соответствовать длинному слову, я думаю.

extension UILabel {
// Adjusts the font size to avoid long word to be wrapped
func fitToAvoidWordWrapping() {
    guard adjustsFontSizeToFitWidth else {
        return // Adjust font only if width fit is needed
    }
    guard let words = text?.components(separatedBy: " ") else {
        return // Get array of words separate by spaces
    }

    // I will need to find the largest word and its width in points
    var largestWord: NSString = ""
    var largestWordWidth: CGFloat = 0

    // Iterate over the words to find the largest one
    for word in words {
        // Get the width of the word given the actual font of the label
        let wordWidth = word.size(withAttributes: [.font: font]).width

        // check if this word is the largest one
        if wordWidth > largestWordWidth {
            largestWordWidth = wordWidth
            largestWord = word as NSString
        }
    }

    // Now that I have the largest word, reduce the label's font size until it fits
    while largestWordWidth > bounds.width && font.pointSize > 1 {
        // Reduce font and update largest word's width
        font = font.withSize(font.pointSize - 1)
        largestWordWidth = largestWord.size(withAttributes: [.font: font]).width
    }
}
}
person Dam    schedule 24.11.2017

Я сделал расширение Swift для UILabel. Просто вызовите метод для метки после установки границ и текста.

extension UILabel {

func fitToAvoidWordWrapping(){
    // adjusts the font size to avoid long word to be wrapped

    // get text as NSString
    let text = self.text ?? "" as NSString

    // get array of words separate by spaces
    let words = text.componentsSeparatedByString(" ") as! [NSString]

    // I will need to find the largest word and its width in points
    var largestWord : NSString = ""
    var largestWordWidth : CGFloat = 0

    // iterate over the words to find the largest one
    for word in words{

        // get the width of the word given the actual font of the label
        let wordSize = word.sizeWithAttributes([NSFontAttributeName : self.font])
        let wordWidth = wordSize.width

        // check if this word is the largest one
        if wordWidth > largestWordWidth{
        largestWordWidth = wordWidth
        largestWord = word
        }
    }

    // now that I have the largest word, reduce the label's font size until it fits
    while largestWordWidth > self.bounds.width && self.font.pointSize > 1{

        // reduce font and update largest word's width
        self.font = self.font.fontWithSize(self.font.pointSize - 1)
        let largestWordSize = largestWord.sizeWithAttributes([NSFontAttributeName : self.font])
        largestWordWidth = largestWordSize.width
    }
}
} 
person J.Williams    schedule 15.06.2015

SWIFT 3 перевод вышеуказанного расширения. Работает как шарм!

extension UILabel {

func fitToAvoidWordWrapping(){
    // adjusts the font size to avoid long word to be wrapped

    // get text as NSString
    let text = self.text ?? ("" as NSString) as String

    // get array of words separate by spaces
    let words = text.components(separatedBy: " ")

    // I will need to find the largest word and its width in points
    var largestWord : NSString = ""
    var largestWordWidth : CGFloat = 0

    // iterate over the words to find the largest one
    for word in words{

        // get the width of the word given the actual font of the label
        let wordSize = word.size(attributes: [NSFontAttributeName : self.font])
        let wordWidth = wordSize.width

        // check if this word is the largest one
        if wordWidth > largestWordWidth{
            largestWordWidth = wordWidth
            largestWord = word as NSString
        }
    }

    // now that I have the largest word, reduce the label's font size until it fits
    while largestWordWidth > self.bounds.width && self.font.pointSize > 1{

        // reduce font and update largest word's width
        self.font = self.font.withSize(self.font.pointSize - 1)
        let largestWordSize = largestWord.size(attributes: [NSFontAttributeName : self.font])
        largestWordWidth = largestWordSize.width
    }
}
}
person Pavel Alexandrovich Ak-01    schedule 01.07.2017