Figure out size of UILabel based on String in Swift
I am trying to calculate the height of a UILabel based on different String lengths.
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
}
Above is the current function I use to determine the height but it is not working. I would greatly appreciate any help I can get. I would perfer the answer in Swift and not Objective C.
Solution 1:
Use an extension on 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)
}
}
and also on NSAttributedString
(which is very useful at times)
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 & 5
Just change the value for attributes
in the extension String
methods
from
[NSFontAttributeName: font]
to
[.font : font]
Solution 2:
For multiline text this answer is not working correctly. You can build a different String extension by using 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
}
}
The UILabel gets a fixed width and the .numberOfLines is set to 0. By adding the text and calling .sizeToFit() it automatically adjusts to the correct height.
Code is written in Swift 3 🔶🐦
Solution 3:
Heres a simple solution thats working for me... similar to some of the others posted, but it doesn't not include the need for calling sizeToFit
Note this is written in 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
This handles line wrapping and such. Not the most elegant code, but it gets the job done.
Solution 4:
This is my answer in Swift 4.1 and 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
}
Now you call this function
//Call this function
let height = heightForView(text: "This is your text", font: UIFont.systemFont(ofSize: 17), width: 300)
print(height)//Output : 41.0
Solution 5:
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)
}
}