คิดขนาดของ UILabel ตาม String ใน Swift


183

ฉันพยายามคำนวณความสูงของ UILabel โดยอิงตามความยาวสตริงที่แตกต่างกัน

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

}

ด้านบนเป็นฟังก์ชั่นปัจจุบันที่ฉันใช้เพื่อกำหนดความสูง แต่มันไม่ทำงาน ฉันซาบซึ้งอย่างยิ่งที่จะได้รับความช่วยเหลือ ฉันจะชอบคำตอบใน Swift ไม่ใช่ Objective C


คำตอบ:


518

ใช้ส่วนขยาย String

สวิฟท์ 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)
    }
}

และบนNSAttributedString(ซึ่งมีประโยชน์มากในบางครั้ง)

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

สวิฟท์ 4

เพียงแค่เปลี่ยนค่าสำหรับattributesในextension Stringวิธีการ

จาก

[NSFontAttributeName: font]

ถึง

[.font : font]

2
@CodyWeaver ตรวจสอบการแก้ไขสำหรับ widthWithConstrainedHeight วิธี
Kaan Dedeoglu

7
@KaanDedeoglu วิธีนี้จะทำงานกับสตริงความสูงแบบไดนามิกเช่นเมื่อคุณใช้ "numberOfLines" = 0 (ซึ่งอาจเป็น UILabel เฉพาะไม่แน่ใจ) หรือ lineBreakMode ByWordWrapping ฉันเดาว่าจะเพิ่มคุณสมบัติดังกล่าว[NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]แต่ไม่ได้ผล
Francisc0

1
คิดว่าฉันคิดคำตอบของฉัน ฉันต้องการใช้NSParagraphStyleAttributeName : styleสไตล์ที่เป็น NSMutableParagraphStyle
Francisc0

4
ฉันต้องเขียน 'self.boundingRect' แทน 'boundingRect' มิฉะนั้นฉันจะได้รับข้อผิดพลาดในการคอมไพล์
Mike M

1
สิ่งหนึ่งที่มีคำตอบนี้ฉันได้พบเมื่อใช้มันในsizeForItemAtIndexPathในUICollectionViewก็คือว่ามันดูเหมือนว่าจะกลับมาแทนที่สำหรับinsetForSectionAt
แซคชาปิโรส์

15

สำหรับข้อความหลายบรรทัดคำตอบนี้ทำงานไม่ถูกต้อง คุณสามารถสร้างส่วนขยายสตริงอื่นได้โดยใช้ 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 มีความกว้างคงที่และ. numberOfLines ถูกตั้งค่าเป็น 0 โดยการเพิ่มข้อความและการโทร. sizeToFit () มันจะปรับความสูงที่ถูกต้องโดยอัตโนมัติ

รหัสถูกเขียนใน Swift 3 🔶🐦


15
sizeToFit แต่แนะนำปัญหาล้าน perfomance เนื่องจากผ่านหลายรูปวาด การคำนวณขนาดด้วยตนเองเป็นวิธีที่ถูกกว่าในทรัพยากร
Marco Pappalardo

2
วิธีแก้ปัญหานี้ควรตั้งค่า UIFont ของ UILabel เพื่อให้มั่นใจในความสูงที่ถูกต้อง
Matthew Spencer

ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน - แม้แต่บัญชีสำหรับสตริงว่างเปล่า (ต่างจากคำตอบที่ยอมรับ) มีประโยชน์มากสำหรับการคำนวณความสูงของ tableView ด้วยเซลล์ความสูงอัตโนมัติ!
Hendies

ฉันพบว่าคำตอบที่ยอมรับนั้นใช้ได้กับความกว้างคงที่ แต่ไม่ใช่ความสูงคงที่ สำหรับความสูงคงที่มันจะเพิ่มความกว้างเพื่อให้พอดีกับทุกสิ่งในหนึ่งบรรทัดยกเว้นว่ามีการแบ่งบรรทัดในข้อความ นี่คือคำตอบอื่นของฉัน: คำตอบของฉัน
MSimic

2
ฉันโพสต์โซลูชันที่คล้ายกัน - โดยไม่ต้องโทรหาsizeToFit
RyanG

6

นี่เป็นวิธีง่ายๆที่ทำงานให้ฉัน ... คล้ายกับบางรายการที่โพสต์ แต่ไม่รวมถึงความต้องการโทร sizeToFit

หมายเหตุนี่เขียนใน 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

นี้จัดการการตัดบรรทัดและเช่น ไม่ใช่รหัสที่หรูหราที่สุด แต่มันทำให้งานเสร็จ


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 และ 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
}

ตอนนี้คุณเรียกใช้ฟังก์ชันนี้

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

1

ฉันพบว่าคำตอบที่ยอมรับนั้นใช้ได้กับความกว้างคงที่ แต่ไม่ใช่ความสูงคงที่ สำหรับความสูงคงที่มันจะเพิ่มความกว้างเพื่อให้พอดีกับทุกสิ่งในหนึ่งบรรทัดยกเว้นว่ามีการแบ่งบรรทัดในข้อความ

ฟังก์ชั่นความกว้างเรียกใช้ฟังก์ชันความสูงหลายครั้ง แต่เป็นการคำนวณอย่างรวดเร็วและฉันไม่ได้สังเกตเห็นปัญหาประสิทธิภาพการทำงานโดยใช้ฟังก์ชันในแถวของ 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
คือminimumTextWrapWidth:CGFloatอะไร
Vyachaslav Gerchicov

มันเป็นเพียงค่าเมล็ดสำหรับการคำนวณในฟังก์ชั่น หากคุณคาดว่าความกว้างจะมีขนาดใหญ่การเลือกค่าน้อยที่สุด TextWrapWidth จะทำให้ลูป while นั้นผ่านการทำซ้ำเพิ่มเติม ดังนั้นความกว้างขั้นต่ำที่ใหญ่กว่าจะดีกว่า แต่ถ้ามันมีขนาดใหญ่กว่าความกว้างจริงที่ต้องการมันก็จะเป็นความกว้างที่ถูกส่งกลับมาเสมอ
MSimic

0

ตรวจสอบความสูงของข้อความป้ายกำกับและกำลังทำงานอยู่

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

วิธีการแก้ปัญหานี้จะช่วยในการคำนวณความสูงและความกว้างที่รันไทม์

    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)

ที่นี่คุณสามารถคำนวณความสูงโดยประมาณที่สายของคุณใช้และส่งผ่านไปยังเฟรม UILabel

estimateFrame.Width
estimateFrame.Height 

0

สวิฟท์ 5:

หากคุณมี UILabel และ soming boundingRect ไม่ได้ผลสำหรับคุณ (ฉันประสบปัญหานี้มันกลับมาที่ความสูง 1 บรรทัดเสมอ) มีส่วนขยายเพื่อคำนวณขนาดของฉลากได้อย่างง่ายดาย

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

คุณสามารถใช้สิ่งนี้:

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

ได้ผลสำหรับฉัน


-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
คำตอบของคุณไม่ควรประกอบด้วยรหัสเท่านั้น แต่ยังรวมถึงคำอธิบายเกี่ยวกับรหัสด้วย โปรดดูวิธีการตอบคำถามสำหรับรายละเอียดเพิ่มเติม
MechMK1

ในขณะที่รหัสนี้อาจตอบคำถาม แต่มีบริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีการที่รหัสนี้ตอบคำถามช่วยปรับปรุงมูลค่าระยะยาว
Isma

คำตอบนี้ไม่สมบูรณ์ มันหมายถึงตัวแปรสำคัญที่ประเภทของใครไม่รู้จักซึ่งเอาชนะวัตถุประสงค์
bitwit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.