Scopri le dimensioni di UILabel in base a String in Swift


183

Sto cercando di calcolare l'altezza di un UILabel in base a diverse lunghezze di stringa.

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

}

Sopra è la funzione corrente che utilizzo per determinare l'altezza ma non funziona. Gradirei molto qualsiasi aiuto che posso ottenere. Preferirei la risposta in Swift e non nell'obiettivo C.


Risposte:


518

Usa un'estensione su 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)
    }
}

e anche su NSAttributedString(che a volte è molto utile)

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

Basta cambiare il valore per attributesnei extension Stringmetodi

a partire dal

[NSFontAttributeName: font]

per

[.font : font]

2
@CodyWeaver controlla la modifica per il metodo widthWithConstrainedHeight.
— Kaan Dedeoglu,

7
@KaanDedeoglu come funzionerebbe con le stringhe di altezza dinamiche come quando usi "numberOfLines" = 0 (che potrebbe essere specifico di UILabel, non sicuro) o lineBreakMode ByWordWrapping. La mia ipotesi era di aggiungerlo agli attributi come questo [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]ma non ha funzionato
— Francisc0

1
Penso di aver capito la mia risposta. Devo usare NSParagraphStyleAttributeName : styledove lo stile è NSMutableParagraphStyle
— Francisc0

4
Devo scrivere "self.boundingRect" invece di "boundingRect", altrimenti visualizzo un errore di compilazione.
— Mike M,

1
Una cosa con questa risposta che ho trovato quando la utilizzavo in sizeForItemAtIndexPatha UICollectionViewè che sembra prevalere sul ritorno perinsetForSectionAt
— Zack Shapiro

15

Per il testo multilinea questa risposta non funziona correttamente. Puoi creare una diversa estensione String usando UILabel

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

UILabel ottiene una larghezza fissa e .numberOfLines è impostato su 0. Aggiungendo il testo e chiamando .sizeToFit () si regola automaticamente all'altezza corretta.

Il codice è scritto in Swift 3 🔶🐦


15
sizeToFit introduce tuttavia un milione di problemi di prestazioni dovuti ai numerosi passaggi del disegno. Il calcolo manuale delle dimensioni è molto più economico in termini di risorse
— Marco Pappalardo,

2
Questa soluzione dovrebbe impostare l'UIFont di UILabel per garantire l'altezza corretta.
— Matthew Spencer,

funziona perfettamente per me, anche per le stringhe vuote (a differenza della risposta accettata). Molto utile per calcolare l'altezza di una tabella Visualizza con celle di altezza automatiche!
— Hendies,

Ho scoperto che la risposta accettata ha funzionato per una larghezza fissa, ma non per un'altezza fissa. Per un'altezza fissa, aumenterebbe semplicemente la larghezza per adattarsi a tutto su una riga, a meno che non ci fosse un'interruzione di riga nel testo. Ecco la mia risposta alternativa: La mia risposta
— MSimic

2
Ho pubblicato una soluzione simile - senza la necessità di una chiamata asizeToFit
— RyanG

6

Ecco una soluzione semplice che funziona per me ... simile ad alcuni degli altri pubblicati, ma non include la necessità di chiamare sizeToFit

Nota che questo è scritto in Swift 5

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

Gestisce il ritorno a capo e così via. Non è il codice più elegante, ma ottiene il lavoro fatto.


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

Questa è la mia risposta in Swift 4.1 e 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
}

Ora chiami questa funzione

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

1

Ho scoperto che la risposta accettata ha funzionato per una larghezza fissa, ma non per un'altezza fissa. Per un'altezza fissa, aumenterebbe semplicemente la larghezza per adattarsi a tutto su una riga, a meno che non ci fosse un'interruzione di riga nel testo.

La funzione larghezza chiama la funzione altezza più volte, ma è un calcolo rapido e non ho notato problemi di prestazioni utilizzando la funzione nelle righe di un UITable.

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
che cos'è minimumTextWrapWidth:CGFloat?
— Vyachaslav Gerchicov,

È solo un valore seed per i calcoli nella funzione. Se si prevede che la larghezza sia grande, selezionando un minimo minimoTextWrapWidth farà sì che il ciclo while passi attraverso iterazioni aggiuntive. Quindi maggiore è la larghezza minima, meglio è, ma se è maggiore della larghezza effettiva richiesta, sarà sempre la larghezza restituita.
— MSimic,

0

Controlla l'altezza del testo dell'etichetta e ci sta lavorando

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

Questa soluzione aiuterà a calcolare l'altezza e la larghezza in fase di esecuzione.

    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)

Qui puoi calcolare l'altezza stimata che la tua stringa prenderebbe e passarla al frame UILabel.

estimateFrame.Width
estimateFrame.Height 

0

Swift 5:

Se hai UILabel e in qualche modo boundingRect non funziona per te (ho riscontrato questo problema. Ha sempre restituito un'altezza di 1 riga.) C'è un'estensione per calcolare facilmente le dimensioni dell'etichetta.

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

Puoi usarlo in questo modo:

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

Per me va bene


-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
La tua risposta non dovrebbe consistere solo in codice, ma anche in una spiegazione relativa al codice. Per ulteriori dettagli, vedere Come rispondere .
— MechMK1

Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo al perché e / o al modo in cui questo codice risponde alla domanda migliora il suo valore a lungo termine.
— Isma,

Questa risposta è incompleta. Si riferisce a variabili importanti di cui non si conoscono i tipi che sconfiggono lo scopo
— bitwit
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.