Finden Sie die Größe von UILabel basierend auf String in Swift heraus


182

Ich versuche, die Höhe eines UILabel basierend auf verschiedenen Stringlängen zu berechnen.

func calculateContentHeight() -> CGFloat{
    var maxLabelSize: CGSize = CGSizeMake(frame.size.width - 48, CGFloat(9999))
    var contentNSString = contentText as NSString
    var expectedLabelSize = contentNSString.boundingRectWithSize(maxLabelSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(16.0)], context: nil)
    print("\(expectedLabelSize)")
    return expectedLabelSize.size.height

}

Oben ist die aktuelle Funktion, mit der ich die Höhe bestimme, aber sie funktioniert nicht. Ich würde mich sehr über jede Hilfe freuen, die ich bekommen kann. Ich würde die Antwort in Swift und nicht in Objective C bevorzugen.


Antworten:


516

Verwenden Sie eine Erweiterung für String

Swift 3

extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.width)
    }
}

und auch auf NSAttributedString(was manchmal sehr nützlich ist)

extension NSAttributedString {
    func height(withConstrainedWidth width: CGFloat) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

        return ceil(boundingBox.width)
    }
}

Swift 4

Ändern Sie einfach den Wert für attributesin den extension StringMethoden

von

[NSFontAttributeName: font]

zu

[.font : font]

2
@CodyWeaver Überprüfen Sie die Bearbeitung auf die widthWithConstrainedHeight-Methode.
Kaan Dedeoglu

7
@KaanDedeoglu Wie würde dies mit dynamischen Höhenzeichenfolgen funktionieren, wenn Sie "numberOfLines" = 0 (möglicherweise UILabel-spezifisch, nicht sicher) oder lineBreakMode ByWordWrapping verwenden. Meine Vermutung war, das zu den Attributen wie diesen hinzuzufügen, [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]aber es hat nicht funktioniert
Francisc0

1
Ich glaube, ich habe meine Antwort herausgefunden. Ich muss verwenden, NSParagraphStyleAttributeName : stylewo Stil ist NSMutableParagraphStyle
Francisc0

4
Ich muss 'self.boundingRect' anstelle von 'boundingRect' schreiben, sonst erhalte ich einen Kompilierungsfehler.
Mike M

1
Eine Sache mit dieser Antwort, die ich gefunden habe, als ich sie in sizeForItemAtIndexPatha verwendete, UICollectionViewist, dass sie die Rückkehr fürinsetForSectionAt
Zack Shapiro

15

Bei mehrzeiligem Text funktioniert diese Antwort nicht richtig. Sie können mit UILabel eine andere String-Erweiterung erstellen

extension String {
func height(constraintedWidth width: CGFloat, font: UIFont) -> CGFloat {
    let label =  UILabel(frame: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude))
    label.numberOfLines = 0
    label.text = self
    label.font = font
    label.sizeToFit()

    return label.frame.height
 }
}

Das UILabel erhält eine feste Breite und die .numberOfLines wird auf 0 gesetzt. Durch Hinzufügen des Texts und Aufrufen von .sizeToFit () wird automatisch die richtige Höhe angepasst.

Code ist in Swift 3 🔶🐦 geschrieben


15
sizeToFit führt jedoch aufgrund der vielen Durchgänge der Zeichnung zu einer Million Leistungsproblemen. Die manuelle Berechnung der Größe ist in Bezug auf Ressourcen viel billiger
Marco Pappalardo

2
Diese Lösung sollte den UIFont des UILabel festlegen, um die richtige Höhe sicherzustellen.
Matthew Spencer

funktioniert perfekt für mich - berücksichtigt sogar leere Zeichenfolgen (im Gegensatz zur akzeptierten Antwort). Sehr praktisch für die Berechnung der Höhe einer Tabellenansicht mit automatischen Höhenzellen!
Hendies

Ich fand heraus, dass die akzeptierte Antwort für eine feste Breite, aber nicht für eine feste Höhe funktionierte. Bei einer festen Höhe würde nur die Breite vergrößert, um alles in eine Zeile zu passen, es sei denn, der Text enthält einen Zeilenumbruch. Hier ist meine alternative Antwort: Meine Antwort
MSimic

2
Ich habe eine ähnliche Lösung veröffentlicht - ohne dass ich anrufen musssizeToFit
RyanG

6

Hier ist eine einfache Lösung, die für mich funktioniert ... ähnlich wie einige der anderen, aber sie beinhaltet nicht die Notwendigkeit eines Anrufs sizeToFit

Beachten Sie, dass dies in Swift 5 geschrieben ist

let lbl = UILabel()
lbl.numberOfLines = 0
lbl.font = UIFont.systemFont(ofSize: 12) // make sure you set this correctly 
lbl.text = "My text that may or may not wrap lines..."

let width = 100.0 // the width of the view you are constraint to, keep in mind any applied margins here

let height = lbl.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel).height

Dies behandelt Zeilenumbrüche und dergleichen. Nicht der eleganteste Code, aber er erledigt den Job.


2
extension String{

    func widthWithConstrainedHeight(_ height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: height)

        let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.width)
    }

    func heightWithConstrainedWidth(_ width: CGFloat, font: UIFont) -> CGFloat? {
        let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.height)
    }

}

1

Dies ist meine Antwort in Swift 4.1 und Xcode 9.4.1

//This is your label
let proNameLbl = UILabel(frame: CGRect(x: 0, y: 20, width: 300, height: height))
proNameLbl.text = "This is your text"
proNameLbl.font = UIFont.systemFont(ofSize: 17)
proNameLbl.numberOfLines = 0
proNameLbl.lineBreakMode = .byWordWrapping
infoView.addSubview(proNameLbl)

//Function to calculate height for label based on text
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat {
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
    label.numberOfLines = 0
    label.lineBreakMode = NSLineBreakMode.byWordWrapping
    label.font = font
    label.text = text

    label.sizeToFit()
    return label.frame.height
}

Jetzt rufen Sie diese Funktion auf

//Call this function
let height = heightForView(text: "This is your text", font: UIFont.systemFont(ofSize: 17), width: 300)
print(height)//Output : 41.0

1

Ich fand heraus, dass die akzeptierte Antwort für eine feste Breite, aber nicht für eine feste Höhe funktionierte. Bei einer festen Höhe würde nur die Breite vergrößert, um alles in eine Zeile zu passen, es sei denn, der Text enthält einen Zeilenumbruch.

Die width-Funktion ruft die height-Funktion mehrmals auf, ist jedoch eine schnelle Berechnung, und ich habe keine Leistungsprobleme bei der Verwendung der Funktion in den Zeilen einer UITable festgestellt.

extension String {

    public func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font : font], context: nil)

        return ceil(boundingBox.height)
    }

    public func width(withConstrainedHeight height: CGFloat, font: UIFont, minimumTextWrapWidth:CGFloat) -> CGFloat {

        var textWidth:CGFloat = minimumTextWrapWidth
        let incrementWidth:CGFloat = minimumTextWrapWidth * 0.1
        var textHeight:CGFloat = self.height(withConstrainedWidth: textWidth, font: font)

        //Increase width by 10% of minimumTextWrapWidth until minimum width found that makes the text fit within the specified height
        while textHeight > height {
            textWidth += incrementWidth
            textHeight = self.height(withConstrainedWidth: textWidth, font: font)
        }
        return ceil(textWidth)
    }
}

1
was ist minimumTextWrapWidth:CGFloat?
Vyachaslav Gerchicov

Es ist nur ein Startwert für die Berechnungen in der Funktion. Wenn Sie erwarten, dass die Breite groß ist, führt die Auswahl eines kleinen MinimumTextWrapWidth dazu, dass die while-Schleife zusätzliche Iterationen durchläuft. Je größer die Mindestbreite, desto besser. Wenn sie jedoch größer als die tatsächlich erforderliche Breite ist, wird immer die zurückgegebene Breite angegeben.
MSimic

0

Überprüfen Sie die Höhe des Etikettentextes und es wird daran gearbeitet

let labelTextSize = ((labelDescription.text)! as NSString).boundingRect(
                with: CGSize(width: labelDescription.frame.width, height: .greatestFiniteMagnitude),
                options: .usesLineFragmentOrigin,
                attributes: [.font: labelDescription.font],
                context: nil).size
            if labelTextSize.height > labelDescription.bounds.height {
                viewMoreOrLess.hide(byHeight: false)
                viewLess.hide(byHeight: false)
            }
            else {
                viewMoreOrLess.hide(byHeight: true)
                viewLess.hide(byHeight: true)

            }

0

Diese Lösung hilft bei der Berechnung der Höhe und Breite zur Laufzeit.

    let messageText = "Your Text String"
    let size = CGSize.init(width: 250, height: 1000)
    let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    let estimateFrame = NSString(string: messageText).boundingRect(with:  size, options: options, attributes: [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue", size: 17)!], context: nil)

Hier können Sie die geschätzte Höhe berechnen, die Ihre Zeichenfolge annehmen würde, und sie an den UILabel-Frame übergeben.

estimateFrame.Width
estimateFrame.Height 

0

Swift 5:

Wenn Sie UILabel haben und BoundingRect für Sie nicht funktioniert (ich hatte dieses Problem. Es wurde immer 1 Zeilenhöhe zurückgegeben.), Gibt es eine Erweiterung, mit der Sie die Etikettengröße einfach berechnen können.

extension UILabel {
    func getSize(constrainedWidth: CGFloat) -> CGSize {
        return systemLayoutSizeFitting(CGSize(width: constrainedWidth, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
    }
}

Sie können es so verwenden:

let label = UILabel()
label.text = "My text\nIs\nAwesome"
let labelSize = label.getSize(constrainedWidth:200.0)

Funktioniert bei mir


-2
@IBOutlet weak var constraintTxtV: NSLayoutConstraint!
func TextViewDynamicallyIncreaseSize() {
    let contentSize = self.txtVDetails.sizeThatFits(self.txtVDetails.bounds.size)
    let higntcons = contentSize.height
    constraintTxtV.constant = higntcons
}

5
Ihre Antwort sollte nicht nur aus Code bestehen, sondern auch aus einer Erklärung zum Code. Weitere Informationen finden Sie unter Antworten .
MechMK1

Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
Isma

Diese Antwort ist unvollständig. Es bezieht sich auf wichtige Variablen, deren Typen unbekannt sind, was den Zweck
zunichte macht
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.