Tìm ra kích thước của UILabel dựa trên String trong Swift


182

Tôi đang cố gắng tính chiều cao của một UILabel dựa trên các độ dài Chuỗi khác nhau.

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

}

Trên đây là chức năng hiện tại tôi sử dụng để xác định chiều cao nhưng nó không hoạt động. Tôi sẽ đánh giá rất cao bất kỳ sự giúp đỡ nào tôi có thể nhận được. Tôi sẽ xem xét câu trả lời trong Swift chứ không phải Objective C.


Câu trả lời:


516

Sử dụng tiện ích mở rộng trên 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)
    }
}

và cũng trên NSAttributedString(đôi khi rất hữu ích)

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

Chỉ cần thay đổi giá trị attributestrong các extension Stringphương thức

từ

[NSFontAttributeName: font]

đến

[.font : font]

2
@CodyWeaver kiểm tra chỉnh sửa cho phương thức widthWithConstrainedHeight.
Kaan Dedeoglu

7
@KaanDedeoglu làm thế nào điều này sẽ hoạt động với các chuỗi chiều cao động như khi bạn sử dụng "numberOfLines" = 0 (có thể là cụ thể của UILabel, không chắc chắn) hoặc lineBreakMode ByWordWrapping. Tôi đoán là thêm nó vào các thuộc tính như thế này [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]nhưng nó không hoạt động
Francisc0

1
Nghĩ rằng tôi đã tìm ra câu trả lời của tôi. Tôi cần sử dụng NSParagraphStyleAttributeName : stylephong cách ở đâu là NSMutablePar ĐoạnStyle
Francisc0

4
Tôi cần phải viết 'self.boundingRect' thay vì 'ràng buộcRect' nếu không tôi sẽ gặp lỗi biên dịch.
Mike M

1
Một điều với câu trả lời này tôi đã tìm thấy khi sử dụng nó trong sizeForItemAtIndexPathmột UICollectionViewlà nó dường như ghi đè lên sự trở lại choinsetForSectionAt
Zack Shapiro

15

Đối với văn bản nhiều dòng, câu trả lời này không hoạt động chính xác. Bạn có thể xây dựng một tiện ích mở rộng Chuỗi khác nhau bằng cách sử dụng 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 có chiều rộng cố định và .numberOfLines được đặt thành 0. Bằng cách thêm văn bản và gọi .sizeToFit (), nó sẽ tự động điều chỉnh theo chiều cao chính xác.

Mã được viết bằng Swift 3


15
Tuy nhiên sizeToFit giới thiệu một triệu vấn đề về độ hoàn hảo do nhiều lần vượt qua bản vẽ. Tính toán kích thước thủ công là cách rẻ hơn về tài nguyên
Marco Pappalardo

2
Giải pháp này nên đặt UIFont của UILabel để đảm bảo chiều cao chính xác.
Matthew Spencer

hoạt động hoàn hảo đối với tôi - thậm chí tài khoản cho các chuỗi trống (không giống như câu trả lời được chấp nhận). Rất thuận tiện để tính chiều cao của một bảng Xem với các ô chiều cao tự động!
Hendies

Tôi thấy rằng câu trả lời được chấp nhận làm việc cho một chiều rộng cố định, nhưng không phải là chiều cao cố định. Đối với một chiều cao cố định, nó sẽ chỉ tăng chiều rộng để phù hợp với mọi thứ trên một dòng, trừ khi có một ngắt dòng trong văn bản. Đây là câu trả lời thay thế của tôi: Câu trả lời của tôi
MSimic

2
Tôi đã đăng một giải pháp tương sizeToFit
tự--

6

Đây là một giải pháp đơn giản phù hợp với tôi ... tương tự như một số giải pháp khác được đăng, nhưng nó không bao gồm nhu cầu gọi điện sizeToFit

Lưu ý điều này được viết bằng 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

Điều này xử lý gói dòng và như vậy. Không phải mã thanh lịch nhất, nhưng nó hoàn thành công việc.


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

Đây là câu trả lời của tôi trong Swift 4.1 và 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
}

Bây giờ bạn gọi chức năng này

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

1

Tôi thấy rằng câu trả lời được chấp nhận làm việc cho một chiều rộng cố định, nhưng không phải là chiều cao cố định. Đối với một chiều cao cố định, nó sẽ chỉ tăng chiều rộng để phù hợp với mọi thứ trên một dòng, trừ khi có một ngắt dòng trong văn bản.

Hàm chiều rộng gọi hàm chiều cao nhiều lần, nhưng đó là phép tính nhanh và tôi không nhận thấy các vấn đề về hiệu suất khi sử dụng hàm trong các hàng của 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

Nó chỉ là một giá trị hạt giống cho các tính toán trong hàm. Nếu bạn kỳ vọng chiều rộng sẽ lớn, việc chọn một mức tối thiểu nhỏWrapWidth sẽ khiến vòng lặp while đi qua các lần lặp bổ sung. Vì vậy, chiều rộng tối thiểu càng lớn càng tốt, nhưng nếu nó lớn hơn chiều rộng thực tế yêu cầu, thì nó sẽ luôn là chiều rộng được trả về.
MSimic

0

Kiểm tra chiều cao văn bản nhãn và nó đang làm việc trên 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

Giải pháp này sẽ giúp tính toán chiều cao và chiều rộng khi chạy.

    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)

Tại đây, bạn có thể tính toán chiều cao ước tính mà chuỗi của bạn sẽ lấy và chuyển nó vào khung UILabel.

estimateFrame.Width
estimateFrame.Height 

0

Swift 5:

Nếu bạn có UILabel và cách nào đó ràng buộcRect không hoạt động cho bạn (Tôi gặp phải vấn đề này. Nó luôn trả về chiều cao 1 dòng.) Có một phần mở rộng để dễ dàng tính kích thước nhãn.

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

Bạn có thể sử dụng nó như thế này:

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

Làm việc cho tôi


-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
Câu trả lời của bạn không chỉ bao gồm mã, mà còn là lời giải thích liên quan đến mã. Vui lòng xem Cách trả lời để biết thêm chi tiết.
MechMK1

Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
Isma

Câu trả lời này không đầy đủ. Nó đề cập đến các biến quan trọng mà các loại không xác định được sẽ đánh bại mục đích
bitwit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.