Swift'teki String'e göre UILabel'in boyutunu belirleme


183

Farklı dize uzunluklarına göre bir UILabel yüksekliğini hesaplamak çalışıyorum.

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

}

Yukarıda, yüksekliği belirlemek için kullandığım geçerli işlev var, ancak çalışmıyor. Alabileceğim her türlü yardımı çok takdir ediyorum. Cevabı C yerine Swift'te görecektim.


Yanıtlar:


519

Tarihinde uzantı kullan String

Hızlı 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)
    }
}

ve ayrıca NSAttributedString(zaman zaman çok faydalıdır)

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

Hızlı 4

Sadece değerini değiştirmek attributesde extension Stringyöntemlere

itibaren

[NSFontAttributeName: font]

için

[.font : font]

2
@CodyWeaver, widthWithConstrainedHeight yöntemi için düzenlemeyi kontrol edin.
Kaan Dedeoğlu

7
@KaanDedeoglu, "numberOfLines" = 0 (UILabel'a özgü olabilir, emin değilsiniz) veya lineBreakMode ByWordWrapping kullandığınızda olduğu gibi dinamik yükseklik dizeleriyle bu nasıl çalışır? Benim tahminim bu gibi özelliklere eklemek oldu [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]ama işe yaramadı
Francisc0

1
Sanırım cevabımı buldum. NSParagraphStyleAttributeName : styleStil NSMutableParagraphStyle
Francisc0

4
'BoundingRect' yerine 'self.boundingRect' yazmak gerekir aksi takdirde derleme hatası alıyorum.
Mike M

1
Bunu kullanırken bulduğum bu cevap bir şey sizeForItemAtIndexPathbir de UICollectionViewbunun için getiriyi geçersiz görünüyor olmasıdırinsetForSectionAt
Zack Shapiro

15

Çok satırlı metin için bu yanıt düzgün çalışmıyor. UILabel kullanarak farklı bir String uzantısı oluşturabilirsiniz

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 sabit bir genişlik alır ve .numberOfLines 0 olarak ayarlanır. Metni ekleyerek ve .sizeToFit () öğesini çağırarak otomatik olarak doğru yüksekliğe ayarlanır.

Kod Swift 3'te yazılmıştır 🔶🐦


15
Ancak sizeToFit, çizimin birçok geçişi nedeniyle bir milyon performans meselesini ortaya koyuyor. Boyutu manuel olarak hesaplamak kaynaklarda çok daha ucuzdur
Marco Pappalardo

2
Bu çözüm, doğru yüksekliği sağlamak için UILabel'in UIFont'unu ayarlamalıdır.
Matthew Spencer

benim için mükemmel çalışıyor - boş dizeleri bile hesaba katıyor (kabul edilen cevabın aksine). Bir tablonun yüksekliğini hesaplamak için çok kullanışlı Otomatik yükseklik hücreleri ile görüntüleyin!
Hendies

Kabul edilen cevabın sabit bir genişlikte çalıştığını, ancak sabit bir yükseklikte olmadığını buldum. Sabit bir yükseklik için, metinde bir satır sonu olmadıkça, genişliği her satıra sığacak şekilde artıracaktır. İşte benim alternatif cevap: Yanıtım
MSimic

2
Benzer bir çözüm yayınladımsizeToFit
RyanG

6

Heres benim için çalışan basit bir çözüm ... yayınlanan diğerlerine benzer, ancak arama ihtiyacını içermiyor sizeToFit

Bunun Swift 5 ile yazılmış olduğunu unutmayın

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

Bu, satır sarmayı ve benzerlerini idare eder. En zarif kod değil, ama işi hallediyor.


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

Swift 4.1 ve Xcode 9.4.1'deki cevabım bu

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

Şimdi bu fonksiyona

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

1

Kabul edilen cevabın sabit bir genişlikte çalıştığını, ancak sabit bir yükseklikte olmadığını buldum. Sabit bir yükseklik için, metinde bir satır sonu olmadıkça, genişliği her satıra sığacak şekilde artıracaktır.

Width işlevi height işlevini birden çok kez çağırır, ancak hızlı bir hesaplamadır ve bir UITable satırındaki işlevi kullanarak performans sorunlarını fark etmedim.

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
nedir minimumTextWrapWidth:CGFloat?
Vyachaslav Gerchicov

Fonksiyondaki hesaplamalar için sadece bir tohum değeridir. Genişliğin büyük olmasını bekliyorsanız, küçük bir minimumTextWrapWidth seçmek while döngüsünün ek yinelemelerden geçmesine neden olur. Böylece, minimum genişlik ne kadar büyük olursa o kadar iyi olur, ancak gereken gerçek genişlikten daha büyükse, her zaman döndürülen genişlik olacaktır.
MSimic

0

Etiket metni yüksekliğini kontrol edin ve üzerinde çalışın

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

Bu çözüm, çalışma zamanında yükseklik ve genişliğin hesaplanmasına yardımcı olacaktır.

    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)

Burada dizenizin alacağı tahmini yüksekliği hesaplayabilir ve UILabel çerçevesine iletebilirsiniz.

estimateFrame.Width
estimateFrame.Height 

0

Hızlı 5:

UILabel'ınız varsa ve someway boundingRect sizin için çalışmıyorsa (bu sorunla karşılaştım. Her zaman 1 satır yüksekliği döndürdüm.) Etiket boyutunu kolayca hesaplamak için bir uzantı var.

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

Bu şekilde kullanabilirsiniz:

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

Benim için çalışıyor


-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
Cevabınız sadece koddan değil, aynı zamanda kodla ilgili bir açıklamadan oluşmalıdır. Daha fazla ayrıntı için lütfen Nasıl Yanıt Verilir'e bakın.
MechMK1

Bu kod soruyu cevaplayabilirken, bu kodun soruyu neden ve / veya nasıl cevapladığı konusunda ek bağlam sağlamak uzun vadeli değerini artırır.
İsma

Bu cevap eksik.
Amaçları
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.