Mencari tahu ukuran UILabel berdasarkan String in Swift

183

Saya mencoba menghitung ketinggian UILabel berdasarkan panjang String yang berbeda.

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

}

Di atas adalah fungsi saat ini yang saya gunakan untuk menentukan ketinggian tetapi tidak berfungsi. Saya akan sangat menghargai bantuan yang bisa saya dapatkan. Saya akan mengalihkan jawaban dalam Swift dan bukan Objective C.

Cody Weaver
sumber
duplikat coba stackoverflow.com/a/61887135/6314955
Malith Kuruwita

Jawaban:

518

Gunakan ekstensi pada String

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

dan juga pada NSAttributedString(yang sangat berguna di kali)

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

Cepat 4

Ubah saja nilai attributesdalam extension Stringmetode

dari

[NSFontAttributeName: font]

untuk

[.font : font]
Kaan Dedeoglu
sumber
2
@CodyWeaver memeriksa metode edit untuk lebarWithConstrainedHeight.
Kaan Dedeoglu
7
@KaanDedeoglu bagaimana ini akan bekerja dengan string ketinggian dinamis seperti ketika Anda menggunakan "numberOfLines" = 0 (yang mungkin spesifik untuk UILabel, tidak yakin) atau lineBreakMode ByWordWrapping. Dugaan saya adalah menambahkan itu ke atribut seperti ini [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]tetapi tidak berhasil
Francisc0
1
Pikir saya menemukan jawaban saya. Saya perlu menggunakan di NSParagraphStyleAttributeName : stylemana gaya adalah NSMutableParagraphStyle
Francisc0
4
Saya perlu menulis 'self.boundingRect' alih-alih 'boundingRect' kalau tidak, saya mendapatkan kesalahan kompilasi.
Mike M
1
Satu hal dengan jawaban ini saya telah menemukan ketika menggunakannya di sizeForItemAtIndexPathdalam UICollectionViewadalah bahwa tampaknya untuk menimpa kembali untukinsetForSectionAt
Zack Shapiro
15

Untuk teks multiline, jawaban ini tidak berfungsi dengan benar. Anda dapat membangun ekstensi String yang berbeda dengan menggunakan 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 mendapatkan lebar tetap dan .numberOfLines diatur ke 0. Dengan menambahkan teks dan memanggil .sizeToFit () secara otomatis menyesuaikan dengan ketinggian yang benar.

Kode ditulis dalam Swift 3 🔶🐦

Sn0wfreeze
sumber
15
Namun sizeToFit memperkenalkan sejuta masalah kinerja karena banyaknya lintasan gambar. Menghitung ukuran secara manual jauh lebih murah dalam sumber daya
Marco Pappalardo
2
Solusi ini harus mengatur UIFont dari UILabel untuk memastikan ketinggian yang benar.
Matthew Spencer
berfungsi dengan baik untuk saya - bahkan menyumbang string kosong (tidak seperti jawaban yang diterima). Sangat berguna untuk menghitung ketinggian tableView dengan sel tinggi otomatis!
Hendies
Saya menemukan bahwa jawaban yang diterima bekerja untuk lebar tetap, tetapi tidak tinggi tetap. Untuk ketinggian tetap, itu hanya akan menambah lebar agar sesuai dengan semua pada satu baris, kecuali jika ada garis putus di teks. Inilah jawaban alternatif saya: Jawaban Saya
MSimic
2
Saya memposting solusi serupa - tanpa perlu panggilan kesizeToFit
RyanG
6

Inilah solusi sederhana yang bekerja untuk saya ... mirip dengan yang lain diposting, tetapi tidak termasuk kebutuhan untuk menelepon sizeToFit

Perhatikan ini ditulis dalam 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

Ini menangani pembungkus garis dan semacamnya. Bukan kode yang paling elegan, tetapi menyelesaikan pekerjaan.

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

}
CrazyPro007
sumber
1

Ini jawaban saya di Swift 4.1 dan 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
}

Sekarang Anda memanggil fungsi ini

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

Saya menemukan bahwa jawaban yang diterima bekerja untuk lebar tetap, tetapi tidak tinggi tetap. Untuk ketinggian tetap, itu hanya akan menambah lebar agar sesuai dengan semua pada satu baris, kecuali jika ada garis putus di teks.

Fungsi lebar memanggil fungsi ketinggian beberapa kali, tetapi ini adalah perhitungan cepat dan saya tidak melihat masalah kinerja menggunakan fungsi di baris 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)
    }
}
MSimic
sumber
1
apa minimumTextWrapWidth:CGFloat?
Vyachaslav Gerchicov
Ini hanya nilai awal untuk perhitungan dalam fungsi. Jika Anda berharap lebarnya menjadi besar, memilih minimum minimum TextWrapWidth akan menyebabkan loop sementara melewati iterasi tambahan. Jadi semakin besar lebar minimum semakin baik, tetapi jika lebih besar dari lebar sebenarnya yang dibutuhkan, maka itu akan selalu menjadi lebar yang dikembalikan.
MSimic
0

Periksa ketinggian teks label dan sedang bekerja di sana

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)

            }
CSE 1994
sumber
0

Solusi ini akan membantu menghitung tinggi dan lebar saat runtime.

    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)

Di sini Anda dapat menghitung perkiraan ketinggian yang akan diambil string Anda dan meneruskannya ke bingkai UILabel.

estimateFrame.Width
estimateFrame.Height 
Shanu Singh
sumber
0

Swift 5:

Jika Anda memiliki UILabel dan entah bagaimana boundingRect tidak berfungsi untuk Anda (saya menghadapi masalah ini. Selalu mengembalikan ketinggian 1 baris.) Ada ekstensi untuk dengan mudah menghitung ukuran label.

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

Anda bisa menggunakannya seperti ini:

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

Bekerja untukku

George Sabanov
sumber
-2
@IBOutlet weak var constraintTxtV: NSLayoutConstraint!
func TextViewDynamicallyIncreaseSize() {
    let contentSize = self.txtVDetails.sizeThatFits(self.txtVDetails.bounds.size)
    let higntcons = contentSize.height
    constraintTxtV.constant = higntcons
}
Niraj Paul
sumber
5
Jawaban Anda tidak hanya terdiri dari kode, tetapi juga penjelasan tentang kode. Silakan lihat Cara Menjawab untuk lebih jelasnya.
MechMK1
Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan meningkatkan nilai jangka panjangnya.
Isma
Jawaban ini tidak lengkap. Ini merujuk ke variabel penting yang tipenya tidak diketahui yang mengalahkan tujuannya
bitwit