ios - font - uilabel size




Рассчитать размер шрифта, чтобы соответствовать рамке-основной текст-NSAttributedString-iOS (8)

В настоящее время принятый ответ говорит об алгоритме, но iOS предоставляет вычисления для объекта NSString. Я бы использовал sizeWithAttributes: из класса NSString .

sizeWithAttributes:

Возвращает размер ограничивающего прямоугольника, который занимает получатель при рисовании с заданными атрибутами.

    - (CGSize)sizeWithAttributes:(NSDictionary *)attributes

Источник: Apple Docs - Справочник по NSString UIKit

РЕДАКТИРОВАТЬ неправильно истолковал вопрос, так что этот ответ не в порядке.

У меня есть текст, который я рисую в фиксированную рамку через NSAttributedString (код ниже). Сейчас я жестко кодирую размер текста до 16. Мой вопрос: есть ли способ рассчитать размер наилучшего соответствия для текста для данного фрейма?

- (void)drawText:(CGContextRef)contextP startX:(float)x startY:(float)
y withText:(NSString *)standString
{
    CGContextTranslateCTM(contextP, 0, (bottom-top)*2);
    CGContextScaleCTM(contextP, 1.0, -1.0);

    CGRect frameText = CGRectMake(1, 0, (right-left)*2, (bottom-top)*2);

    NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:standString];
    [attrString addAttribute:NSFontAttributeName
                      value:[UIFont fontWithName:@"Helvetica-Bold" size:16.0]
                      range:NSMakeRange(0, attrString.length)];

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));
    struct CGPath * p = CGPathCreateMutable();
    CGPathAddRect(p, NULL, frameText);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL);

    CTFrameDraw(frame, contextP);
}

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

func binarySearchOptimalFontSize(min: Int, max: Int) -> Int {
    let middleSize = (min + max) / 2

    if min > max {
        return middleSize
    }

    let middleFont = UIFont(name: font!.fontName, size: CGFloat(middleSize))!

    let attributes = [NSFontAttributeName : middleFont]
    let attributedString = NSAttributedString(string: text, attributes: attributes)

    let size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
    let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
    let textSize = attributedString.boundingRect(with: size, options: options, context: nil)

    if textSize.size.equalTo(bounds.size) {
        return middleSize
    } else if (textSize.height > bounds.size.height || textSize.width > bounds.size.width) {
        return binarySearchOptimalFontSize(min: min, max: middleSize - 1)
    } else {
        return binarySearchOptimalFontSize(min: middleSize + 1, max: max)
    }
}

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


Вот мое решение в Swift 4:

private func adjustedFontSizeOf(label: UILabel) -> CGFloat {
    guard let textSize = label.text?.size(withAttributes: [.font: label.font]), textSize.width > label.bounds.width else {
        return label.font.pointSize
    }

    let scale = label.bounds.width / textSize.width
    let actualFontSize = scale * label.font.pointSize

    return actualFontSize
}

Надеюсь, это кому-нибудь поможет.


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

UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = @"Some text";
float largestFontSize = 12;
while ([label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:largestFontSize]}].width > modifierFrame.size.width)
{
     largestFontSize--;
}
label.font = [UIFont systemFontOfSize:largestFontSize];

Вы можете установить для свойства adjustsFontSizeToFitWidth значение YES в соответствии с документацией Apple


Единственный способ убедиться, что это возможно, - это иметь систему, которая выполняет вычисление размера, затем корректирует размер и повторяет его, пока не найдет нужный размер.

Т.е. настроить алгоритм деления пополам между определенными размерами.

т.е. запустить его для размера 10. Слишком маленький. Размер 20. Слишком маленький. Размер 30. Слишком большой. Размер 25. Слишком маленький. Размер 27. Точно, используйте размер 27.

Вы могли бы даже начать сотни.

Размер 100. Слишком большой. Размер 50. и т. Д ...


Мне нравится подход @holtwick, но я обнаружил, что иногда он переоценивает то, что подходит. Я создал твик, который, кажется, хорошо работает в моих тестах. Совет: не забудьте протестировать с действительно широкими буквами, такими как "WWW" или даже "௵௵௵"

func idealFontSize(for text: String, font: UIFont, width: CGFloat) -> CGFloat {
    let baseFontSize = CGFloat(256)
    let textSize = text.size(attributes: [NSFontAttributeName: font.withSize(baseFontSize)])
    let ratio = width / textSize.width

    let ballparkSize = baseFontSize * ratio
    let stoppingSize = ballparkSize / CGFloat(2) // We don't want to loop forever, if we've already come down to 50% of the ballpark size give up
    var idealSize = ballparkSize
    while (idealSize > stoppingSize && text.size(attributes: [NSFontAttributeName: font.withSize(idealSize)]).width > width) {
        // We subtract 0.5 because sometimes ballparkSize is an overestimate of a size that will fit
        idealSize -= 0.5
    }

    return idealSize
}

Небольшая хитрость помогает использовать sizeWithAttributes: без необходимости итерации для получения правильного результата:

NSSize sampleSize = [wordString sizeWithAttributes:
    @{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }];
CGFloat ratio = rect.size.width / sampleSize.width;
fontSize *= ratio;

Убедитесь, что fontSize для образца достаточно велик, чтобы получить хорошие результаты.





nsattributedstring