Jak obliczyć szerokość ciągu tekstowego określonej czcionki i rozmiar czcionki?


82

Mam UILabel, który wyświetla kilka znaków. Na przykład „x”, „y” lub „rpm”. Jak mogę obliczyć szerokość tekstu na etykiecie (nie wykorzystuje całej dostępnej przestrzeni)? Służy do automatycznego układania, gdzie inny widok będzie miał większy prostokąt ramki, jeśli ten UILabel ma mniejszy tekst w środku. Czy istnieją metody obliczania tej szerokości tekstu, gdy określono UIFont i rozmiar czcionki? Nie ma też podziału wiersza i tylko jednej linii.


Nie wiem, jak byś to zrobił z dowolnym rodzajem czcionki, jednak jeśli używasz czcionki o stałej szerokości, możesz obliczyć, używając liczby znaków. Nie jestem do końca pewien wzoru.
jgallant

Odpowiedzi:


74

Możesz to zrobić dokładnie za pomocą różnych sizeWithFont:metod w NSString UIKit Additions . W twoim przypadku najprostszy wariant powinien wystarczyć (ponieważ nie masz etykiet wieloliniowych):

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

Istnieje kilka odmian tej metody, np. niektórzy rozważają tryby łamania linii lub maksymalne rozmiary.


1
Czy nie powinien to być UIFont * yourFont = // [UIFont ...]; chociaż?
PinkFloydRocks

11
„sizeWithFont:” jest przestarzałe. Odpowiedź przez wcochrana powinna być zaznaczona.
Ferran Maylinch

4
Ponieważ masz zielony znacznik wyboru, powinieneś zmienić odpowiedź, aby użyć sizeWithAttributes.
Glenn Howes,

80

Ponieważ sizeWithFontjest przestarzały, po prostu zaktualizuję moją pierwotną odpowiedź na używanie Swift 4 i.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)
}

Powinien to być rozmiar ekranu „Twój tekst tutaj” w punktach.


Dziękuję za wysłanie tego rozwiązania. Napisałem rozszerzenie na podstawie Twojej odpowiedzi. Jest zamieszczone poniżej.
Adrian

Jaka będzie odpowiednia funkcja w Swift 4?
Swayambhu

77

sizeWithFont:jest przestarzały, użyj sizeWithAttributes:zamiast tego:

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

8
Uwaga: sizeWithAttributes:metoda zwraca rozmiary ułamkowe; aby użyć zwróconego rozmiaru do rozmiaru widoków, należy podnieść jego wartość do najbliższej większej liczby całkowitej za pomocą funkcji ceil.
Allen

55

Aktualizacja z września 2019 r

Ta odpowiedź jest dużo czystszym sposobem na zrobienie tego przy użyciu nowej składni.

Oryginalna odpowiedź

W oparciu o doskonałą odpowiedź Glenna Howesa stworzyłem rozszerzenie do obliczania szerokości ciągu. Jeśli robisz coś takiego, jak ustawienie szerokości a UISegmentedControl, może to ustawić szerokość na podstawie ciągu tytułu segmentu.

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

stosowanie:

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

1
Jakie będzie rozwiązanie dla Swift 4?
Swayambhu

1
Dlaczego nie tylko jedna funkcja, która zwraca rozmiar (CGSize)? Dlaczego pracuję dwa razy?
wcochran

@wcochran Zaktualizowano. Świetna rozmowa.
Adrian

1
Nie wiem jak, ale to daje mi nieprawidłowe rozmiary.
Max

Nieważne, to działa, ale z moich testów wynika, że ​​musisz dodać dodatkowe wypełnienie co najmniej 15 , aby zapobiec obcięciu tekstu.
Max

33

Swift-5

Użyj intrinsicContentSize, aby znaleźć wysokość i szerokość tekstu.

yourLabel.intrinsicContentSize.width

To zadziała, nawet jeśli masz niestandardowe odstępy między ciągami, takie jak „TEX T”


28

Oneliner w Swift 4.2 🔸

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

6

To proste rozszerzenie w Swift działa dobrze.

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

Stosowanie:

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

Szybki 4

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

2

To jest dla szybkiej wersji 2.3. Możesz uzyskać szerokość sznurka.

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

Jeśli masz problemy z uzyskaniem szerokości tekstu z obsługą wielu wierszy , możesz użyć następnego kodu ( 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
}

I w ten sam sposób możesz znaleźć wysokość tekstu, jeśli musisz (po prostu przełącz implementację constraintBox):

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

Lub tutaj jest ujednolicona funkcja do uzyskiwania rozmiaru tekstu z obsługą wielu wierszy:

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
}

Stosowanie:

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

1

Dla 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;
   }
}

Używaj go jak ...

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

1

Nie jestem pewien, jak wydajne jest to, ale napisałem tę funkcję, która zwraca rozmiar w punktach, który dopasuje ciąg do określonej szerokości:

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
}

I zostałby użyty w ten sposób:

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


0

Mój kod polega na utworzeniu rozszerzenia UIFont: (To jest Swift 4.1)

extension UIFont {


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

} // extension UIFont
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.