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.