在Swift中基于String找出UILabel的大小


182

我正在尝试根据不同的String长度来计算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中给出答案。


Answers:


517

在上使用扩展名 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

只要改变值attributesextension String方法

[NSFontAttributeName: font]

[.font : font]

2
@CodyWeaver检查widthWithConstrainedHeight方法的编辑。
Kaan Dedeoglu 2015年

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在a中使用它时发现的这个答案的一件事UICollectionView是,它似乎覆盖了insetForSectionAt
Zack Shapiro

15

对于多行文本,此答案无法正常工作。您可以使用UILabel构建其他String扩展

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引入了上百万个性能问题。手动计算大小可以
节省

2
此解决方案应设置UILabel的UIFont以确保正确的高度。
马修·斯宾塞

非常适合我-甚至考虑了空字符串(与接受的答案不同)。使用自动高度单元来计算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

我发现,可接受的答案适用于固定宽度,但不适用于固定高度。对于固定高度,除非文本中有换行符,否则它只会增加宽度以适合一行中的所有内容。

width函数多次调用height函数,但这是一种快速计算,并且我没有注意到在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

它只是函数中计算的种子值。如果您希望宽度较大,则选择较小的minimumTextWrapWidth会导致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,并且某种程度上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,2017年

这个答案是不完整的。它指的是类型未知的重要变量,这些变量的类型未知,从而
无法
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.