Wie berechnet man die Breite einer Textzeichenfolge einer bestimmten Schriftart und Schriftgröße?


82

Ich habe ein UILabel, das einige Zeichen anzeigt. Wie "x", "y" oder "U / min". Wie kann ich die Breite des Textes auf dem Etikett berechnen (es wird nicht der gesamte verfügbare Platz beansprucht)? Dies ist für das automatische Layout vorgesehen, bei dem eine andere Ansicht ein größeres Rahmenrechteck aufweist, wenn das UILabel einen kleineren Text enthält. Gibt es Methoden zum Berechnen dieser Textbreite, wenn ein UIFont und eine Schriftgröße angegeben werden? Es gibt auch keinen Zeilenumbruch und nur eine einzige Zeile.


Ich weiß nicht, wie Sie dies mit einem Schrifttyp tun würden. Wenn Sie jedoch eine Schriftart mit fester Breite verwenden, können Sie anhand der Anzahl der Zeichen berechnen. Ich bin mir der Formel nicht ganz sicher.
Jgallant

Antworten:


72

Sie können genau das über die verschiedenen sizeWithFont:Methoden in NSString UIKit Additions tun . In Ihrem Fall sollte die einfachste Variante ausreichen (da Sie keine mehrzeiligen Beschriftungen haben):

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

Es gibt verschiedene Variationen dieser Methode, z. Einige berücksichtigen Zeilenumbruchmodi oder maximale Größen.


1
Sollte es nicht UIFont sein * yourFont = // [UIFont ...]; obwohl?
PinkFloydRocks

Ups, ja, sicher. Behoben.
Daniel Rinser

11
"sizeWithFont:" ist veraltet. Die Antwort von wcochran sollte markiert sein.
Ferran Maylinch

4
Da Sie das grüne Häkchen haben, sollten Sie Ihre Antwort ändern, um sizeWithAttributes zu verwenden.
Glenn Howes

80

Da sizeWithFontes veraltet ist, werde ich nur meine ursprüngliche Antwort auf die Verwendung von Swift 4 und aktualisieren.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)
}

Die Größe sollte die Bildschirmgröße von "Ihr Text hier" in Punkten sein.


Vielen Dank, dass Sie diese Lösung veröffentlicht haben. Ich habe eine Erweiterung basierend auf Ihrer Antwort geschrieben. Es ist unten veröffentlicht.
Adrian

Was wird die entsprechende Funktion in Swift 4 sein?
Swayambhu

77

sizeWithFont:ist jetzt veraltet, verwenden Sie sizeWithAttributes:stattdessen:

UIFont *font = [UIFont fontWithName:@"Helvetica" size:30];
NSDictionary *userAttributes = @{NSFontAttributeName: font,
                                 NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *text = @"hello";
...
const CGSize textSize = [text sizeWithAttributes: userAttributes];

8
Hinweis: Die sizeWithAttributes:Methode gibt Bruchgrößen zurück. Um eine zurückgegebene Ansicht von Größe zu Größe zu verwenden, müssen Sie ihren Wert mithilfe der Ceil-Funktion auf die nächsthöhere Ganzzahl erhöhen.
Allen

55

Update September 2019

Diese Antwort ist eine viel sauberere Methode, um sie mit neuer Syntax zu erstellen.

Ursprüngliche Antwort

Basierend auf Glenn Howes 'hervorragender Antwort habe ich eine Erweiterung erstellt, um die Breite einer Zeichenfolge zu berechnen. Wenn Sie beispielsweise die Breite von a UISegmentedControlfestlegen, kann dies die Breite basierend auf der Titelzeichenfolge des Segments festlegen.

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

Verwendung:

    // Set width of segmentedControl
    let starString = "⭐️"
    let starWidth = starString.widthOfString(usingFont: UIFont.systemFont(ofSize: 14)) + 16
    segmentedController.setWidth(starWidth, forSegmentAt: 3)

1
Was wird die Lösung für Swift 4 sein?
Swayambhu

1
Warum nicht nur eine Funktion, die Größe zurückgibt (CGSize)? Warum zweimal arbeiten?
wcochran

@wcochran Aktualisiert. Toller Anruf.
Adrian

1
Ich weiß nicht wie, aber das gibt mir falsche Größen.
Max

Egal, das funktioniert, aber nach meinen Tests müssen Sie einen zusätzlichen Abstand von mindestens 15 hinzufügen, um zu verhindern, dass der Text abgeschnitten wird.
Max

31

Swift-5

Verwenden Sie intrinsicContentSize, um die Höhe und Breite des Texts zu ermitteln.

yourLabel.intrinsicContentSize.width

Dies funktioniert auch dann, wenn Sie einen benutzerdefinierten Abstand zwischen Ihrer Zeichenfolge haben, z. B. "TEX T".


25

Oneliner in Swift 4.2 🔸

let size = text.size(withAttributes:[.font: UIFont.systemFont(ofSize:18.0)])

6

Diese einfache Erweiterung in Swift funktioniert gut.

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

Verwendung:

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

Swift 4

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

2

Dies ist für die schnelle Version 2.3. Sie können die Breite der Zeichenfolge erhalten.

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

Wenn Sie Schwierigkeiten haben, die Textbreite mit mehrzeiliger Unterstützung zu erreichen , können Sie den nächsten Code verwenden ( 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
}

Und auf die gleiche Weise können Sie bei Bedarf die Texthöhe ermitteln (wechseln Sie einfach die ConstraintBox-Implementierung):

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

Oder hier ist eine einheitliche Funktion zum Abrufen der Textgröße mit mehrzeiliger Unterstützung:

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
}

Verwendung:

let textSize = labelSize(for: "SomeText", maxWidth: contentView.bounds.width, maxHeight: .greatestFiniteMagnitude)
let textHeight = textSize.height.rounded(.up)
let textWidth = textSize.width.rounded(.up)

1

Für 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;
   }
}

Verwenden Sie es wie ...

        let Str = "ABCDEF"
        let Font =  UIFont.systemFontOfSize(19.0)
        let SizeOfString = Str.SizeOfString(font: Font!)

1

Ich bin mir nicht sicher, wie effizient dies ist, aber ich habe diese Funktion geschrieben, die die Punktgröße zurückgibt, die einer Zeichenfolge auf eine bestimmte Breite passt:

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
}

Und es würde so verwendet werden:

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


0

Mein Code besteht darin, eine Erweiterung von UIFont zu erstellen: (Dies ist Swift 4.1)

extension UIFont {


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

} // extension UIFont
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.