Як розрахувати ширину текстового рядка певного шрифту та розміру шрифту?


82

У мене є UILabel, який відображає деякі символи. Як "x", "y" або "rpm". Як я можу розрахувати ширину тексту в мітці (вона не використовує весь доступний простір)? Це для автоматичного макетування, де інший вигляд матиме більший прямокутник кадру, якщо цей UILabel має менший текст всередині. Чи існують методи обчислення такої ширини тексту, коли вказано UIFont і розмір шрифту? Також немає розриву рядка і є лише один рядок.


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

Відповіді:


74

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

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

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


1
Чи не має бути UIFont * yourFont = // [UIFont ...]; хоча?
PinkFloydRocks

11
"sizeWithFont:" застаріло. Відповідь wcochran має бути позначеною.
Ферран Мейлінч

4
Оскільки у вас є зелений прапорець, вам слід змінити свою відповідь на sizeWithAttributes.
Гленн Хоус

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)
}

Розмір повинен відповідати розміру на екрані "Ваш текст тут" у балах.


Дякуємо за розміщення цього рішення. Я написав розширення на основі вашої відповіді. Це розміщено нижче.
Адріан

Якою буде відповідна функція в swift 4?
Swaambhu

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];

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?
Swaambhu

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}

3

Стрімкий 4

extension String {
    func SizeOf(_ font: UIFont) -> CGSize {
        return self.size(withAttributes: [NSAttributedStringKey.font: font])
    }
}

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