Как рассчитать ширину текстовой строки определенного шрифта и размера шрифта?

82

У меня есть UILabel, который отображает некоторые символы. Как «x», «y» или «rpm». Как я могу рассчитать ширину текста на этикетке (она не занимает все доступное пространство)? Это для автоматического макета, когда другое представление будет иметь прямоугольник кадра большего размера, если внутри этого UILabel текст меньшего размера. Есть ли методы для расчета этой ширины текста, когда указаны UIFont и размер шрифта? Также нет разрыва строки и только одна строка.


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

Ответы:

74

Вы можете сделать это с помощью различных sizeWithFont:методов в дополнениях NSString UIKit . В вашем случае будет достаточно простейшего варианта (поскольку у вас нет многострочных меток):

NSString *someString = @"Hello World";
UIFont *yourFont = // [UIFont ...]
CGSize stringBoundingBox = [someString sizeWithFont:yourFont];

Есть несколько вариантов этого метода, например. некоторые рассматривают режимы разрыва строки или максимальные размеры.

Даниэль Ринзер
источник
1
Разве это не должно быть UIFont * yourFont = // [UIFont ...]; хотя?
PinkFloydRocks
11
"sizeWithFont:" устарел. Ответ wcochran должен быть отмечен.
Ферран Мэйлинч
4
Поскольку у вас есть зеленая галочка, вам следует изменить свой ответ, чтобы использовать sizeWithAttributes.
Glenn Howes,
80

Поскольку sizeWithFontон устарел, я просто собираюсь обновить свой исходный ответ на использование Swift 4 и.size

//: Playground - noun: a place where people can play

import UIKit

if let font = UIFont(name: "Helvetica", size: 24) {
   let fontAttributes = [NSAttributedStringKey.font: font]
   let myText = "Your Text Here"
   let size = (myText as NSString).size(withAttributes: fontAttributes)
}

Размер должен соответствовать экранному размеру "Your Text Here" в пунктах.

Гленн Хоус
источник
Спасибо, что разместили это решение. Я написал расширение на основе вашего ответа. Это размещено ниже.
Адриан
Какая будет соответствующая функция в swift 4?
Сваямбху
77

sizeWithFont:теперь устарело, используйте sizeWithAttributes:вместо этого:

UIFont *font = [UIFont fontWithName:@"Helvetica" size:30];
NSDictionary *userAttributes = @{NSFontAttributeName: font,
                                 NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *text = @"hello";
...
const CGSize textSize = [text sizeWithAttributes: userAttributes];
Wcochran
источник
8
Примечание: sizeWithAttributes:метод возвращает дробные размеры; чтобы использовать возвращаемый размер для размера представлений, вы должны поднять его значение до ближайшего большего целого числа с помощью функции ceil.
Аллен
55

Обновление сентябрь 2019 г.

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

Оригинальный ответ

Основываясь на отличном ответе Гленна Хауза , я создал расширение для вычисления ширины строки. Если вы делаете что-то вроде установки ширины a UISegmentedControl, это может установить ширину на основе строки заголовка сегмента.

extension String {

    func widthOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }

    func heightOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.height
    }

    func sizeOfString(usingFont font: UIFont) -> CGSize {
        let fontAttributes = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: fontAttributes)
    }
}

Применение:

    // Set width of segmentedControl
    let starString = "⭐️"
    let starWidth = starString.widthOfString(usingFont: UIFont.systemFont(ofSize: 14)) + 16
    segmentedController.setWidth(starWidth, forSegmentAt: 3)
Адриан
источник
1
Каким будет решение для Swift 4?
Сваямбху
1
Почему не только одна функция, возвращающая размер (CGSize)? Зачем работать дважды?
wcochran
@wcochran Обновлено. Отличный звонок.
Адриан
1
Не знаю как, но это дает мне неправильные размеры.
Макс
Неважно, это работает, но из моих тестов вам нужно добавить дополнительные отступы не менее 15 , чтобы текст не усекался.
Макс
33

Стриж-5

Используйте intrinsicContentSize, чтобы найти высоту и ширину текста.

yourLabel.intrinsicContentSize.width

Это будет работать, даже если у вас есть настраиваемый интервал между строками, например "TEX T"

Алок
источник
28

Oneliner в Swift 4.2 🔸

let size = text.size(withAttributes:[.font: UIFont.systemFont(ofSize:18.0)])
эонист
источник
6

Это простое расширение в Swift работает хорошо.

extension String {
    func size(OfFont font: UIFont) -> CGSize {
        return (self as NSString).size(attributes: [NSFontAttributeName: font])
    }
}

Применение:

let string = "hello world!"
let font = UIFont.systemFont(ofSize: 12)
let width = string.size(OfFont: font).width // size: {w: 98.912 h: 14.32}
JsW
источник
3

Swift 4

extension String {
    func SizeOf(_ font: UIFont) -> CGSize {
        return self.size(withAttributes: [NSAttributedStringKey.font: font])
    }
}
WM
источник
2

Это для быстрой версии 2.3. Вы можете получить ширину строки.

var sizeOfString = CGSize()
if let font = UIFont(name: "Helvetica", size: 14.0)
    {
        let finalDate = "Your Text Here"
        let fontAttributes = [NSFontAttributeName: font] // it says name, but a UIFont works
        sizeOfString = (finalDate as NSString).sizeWithAttributes(fontAttributes)
    }
Мандип Сингх
источник
2

Если вам не удается получить ширину текста с помощью многострочной поддержки , вы можете использовать следующий код ( Swift 5 ):

func width(text: String, height: CGFloat) -> CGFloat {
    let attributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 17)
    ]
    let attributedText = NSAttributedString(string: text, attributes: attributes)
    let constraintBox = CGSize(width: .greatestFiniteMagnitude, height: height)
    let textWidth = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).width.rounded(.up)

    return textWidth
}

Точно так же вы можете найти высоту текста, если вам нужно (просто переключите реализацию constraintBox):

let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude)

Или вот унифицированная функция для получения размера текста с поддержкой многострочного текста:

func labelSize(for text: String, maxWidth: CGFloat, maxHeight: CGFloat) -> CGSize {
    let attributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 17)
    ]

    let attributedText = NSAttributedString(string: text, attributes: attributes)

    let constraintBox = CGSize(width: maxWidth, height: maxHeight)
    let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral

    return rect.size
}

Применение:

let textSize = labelSize(for: "SomeText", maxWidth: contentView.bounds.width, maxHeight: .greatestFiniteMagnitude)
let textHeight = textSize.height.rounded(.up)
let textWidth = textSize.width.rounded(.up)
атерешков
источник
1

Для Swift 3.0+

extension String {
    func SizeOf_String( font: UIFont) -> CGSize {
        let fontAttribute = [NSFontAttributeName: font]
        let size = self.size(attributes: fontAttribute)  // for Single Line
       return size;
   }
}

Используйте это как ...

        let Str = "ABCDEF"
        let Font =  UIFont.systemFontOfSize(19.0)
        let SizeOfString = Str.SizeOfString(font: Font!)
Лахдип Сингх
источник
1

Не уверен, насколько это эффективно, но я написал эту функцию, которая возвращает размер точки, который соответствует ширине строки:

func fontSizeThatFits(targetWidth: CGFloat, maxFontSize: CGFloat, font: UIFont) -> CGFloat {
    var variableFont = font.withSize(maxFontSize)
    var currentWidth = self.size(withAttributes: [NSAttributedString.Key.font:variableFont]).width

    while currentWidth > targetWidth {
        variableFont = variableFont.withSize(variableFont.pointSize - 1)
        currentWidth = self.size(withAttributes: [NSAttributedString.Key.font:variableFont]).width
    }

    return variableFont.pointSize
}

И он будет использоваться так:

textView.font = textView.font!.withSize(textView.text!.fontSizeThatFits(targetWidth: view.frame.width, maxFontSize: 50, font: textView.font!))

Давид Шопен
источник
0

Мой код - это расширение UIFont: (это Swift 4.1).

extension UIFont {


    public func textWidth(s: String) -> CGFloat
    {
        return s.size(withAttributes: [NSAttributedString.Key.font: self]).width
    }

} // extension UIFont
МаркАурелиус
источник