Reputation: 3784
I am drawing UILabels
programmatically. They get their sizes from a database. So I cannot just use sizeToFit
. I have already implemented a function that redraws UILabels
with a passed ratio. So all I need to find is the text in UILabel
from my view that would require the maximum ratio to redraw UILabels
.
So finally I need to do something like this:
double ratio = 1.00;
for (UILabel* labels in sec.subviews) {
float widthLabel = labels.frame.size.width;
float heightLabel = labels.frame.size.height;
float heightText = //get the text height here
float widthText = //get the text width here
if (widthLabel < widthText) {
ratio = MAX(widthText/widthLabel,ratio);
}
if (heightLabel < heightText) {
ratio = MAX(heightText/heightLabel, ratio);
}
}
//redraw UILabels with the given ratio here
So how can I get the height and width size of a text, as some of my text do not fit into the label I cannot simply use label bounds? I am using Xcode 5 and iOS 7.
Upvotes: 57
Views: 100773
Reputation: 12615
Swift 5:
func getTextBounds(_ label : UILabel) -> CGRect {
if label.text != nil && label.font != nil {
return label.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : label.font!],
context: nil)
}
return CGRect.null
}
func getTextBounds(_ textField : UITextField) -> CGRect {
if textField.text != nil && textField.font != nil {
return textField.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : textField.font!],
context: nil)
}
return CGRect.null
}
Or, as extensions:
extension UILabel {
func textBounds() -> CGRect {
if self.text != nil && self.font != nil {
return self.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : self.font!],
context: nil)
}
return CGRect.null
}
}
extension UITextField {
func textBounds() -> CGRect {
if self.text != nil && self.font != nil {
return self.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : self.font!],
context: nil)
}
return CGRect.null
}
}
Upvotes: 0
Reputation: 5806
It's a really ugly mess given that if you set UILabel font after you have set it with attributedString it clobbers the font info in attributed text and you have to compute based on text+font attributes
Something to the tune of
CGFloat promptLabelMaxWidth = self.promptLabel.frame.size.width;
NSAttributedString *attributedText = self.promptLabel.attributedText;
assert(attributedText);
CGRect rect = [attributedText boundingRectWithSize:(CGSize){promptLabelMaxWidth, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];
NSString *text = self.promptLabel.text;
UIFont *font = self.promptLabel.font;
if (font) {
CGRect r = [text boundingRectWithSize: CGSizeMake(promptLabelMaxWidth, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font}
context:nil];
if (r.size.height > rect.size.height) {
rect = r;
}
}
Upvotes: 0
Reputation: 955
A solution that works with multiline labels (Swift 4), to calculate the height from a fixed width:
let label = UILabel(frame: .zero)
label.numberOfLines = 0 // multiline
label.font = UIFont.systemFont(ofSize: UIFont.labelFontSize) // your font
label.preferredMaxLayoutWidth = width // max width
label.text = "This is a sample text.\nWith a second line!" // the text to display in the label
let height = label.intrinsicContentSize.height
Upvotes: 2
Reputation: 4739
Swift 3.0
func getLabelHeight() -> CGFloat {
let font = UIFont(name: "OpenSans", size: 15)!
let textString = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." as NSString
let textAttributes = [NSFontAttributeName: font]
let rect = textString.boundingRect(with: CGSize(width: 320, height: 2000), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
return rect.size.height
}
Upvotes: 0
Reputation: 1046
Length gets the number of characters. If you want to get the width of the text:
Objective-C
CGSize textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[label font]}];
Swift 4
let size = label.text?.size(withAttributes: [.font: label.font]) ?? .zero
This gets you the size. And you can compare the textSize.width
of each label.
Upvotes: 51
Reputation: 6760
Here is a swift variant.
let font = UIFont(name: "HelveticaNeue", size: 25)!
let text = "This is some really long text just to test how it works for calculating heights in swift of string sizes. What if I add a couple lines of text?"
let textString = text as NSString
let textAttributes = [NSFontAttributeName: font]
textString.boundingRectWithSize(CGSizeMake(320, 2000), options: .UsesLineFragmentOrigin, attributes: textAttributes, context: nil)
Upvotes: 18
Reputation: 21967
All of the [NSString sizeWithFont...]
methods are deprecated in iOS 7. Use this instead.
CGRect labelRect = [text
boundingRectWithSize:labelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{
NSFontAttributeName : [UIFont systemFontOfSize:14]
}
context:nil];
Also see https://developer.apple.com/documentation/foundation/nsstring/1619914-sizewithfont.
Per your comment I did a simple test. The code and output is below.
// code to generate a bounding rect for text at various font sizes
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
for (NSNumber *n in @[@(12.0f), @(14.0f), @(18.0f)]) {
CGFloat fontSize = [n floatValue];
CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
NSLog(@"fontSize = %f\tbounds = (%f x %f)",
fontSize,
r.size.width,
r.size.height);
}
this produces the following output (note that the bounds change as expected as the font size gets larger):
fontSize = 12.000000 bounds = (181.152008 x 28.632000)
fontSize = 14.000000 bounds = (182.251999 x 50.105999)
fontSize = 18.000000 bounds = (194.039993 x 64.421997)
Upvotes: 69
Reputation: 3317
msgStr string get size :
let msgStr:NSString = Data["msg"]! as NSString
let messageSize = msgStr.boundingRect(with: CGSize(width: ChatTable.frame.width-116, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont(name: "Montserrat-Light", size: 14)!], context: nil).size
Upvotes: 0
Reputation: 752
By using this line of code we can get the size of text on the label.
let str = "Sample text"
let size = str.sizeWithAttributes([NSFontAttributeName:UIFont.systemFontOfSize(17.0)])
So, we can use the both width and height.
Upvotes: 1
Reputation: 1640
Another simple way to do this that I haven't seen mentioned yet:
CGSize textSize = [label intrinsicContentSize];
(This only works correctly after you have set the label's text and font, of course.)
Upvotes: 30
Reputation: 12215
Little advice guys, if like me you're using, boundingRectWithSize
with [UIFont systemFontOFSize:14]
If your string is two lines long, the returned rect height is something like 33,4 points.
Don't make the mistake, like me, to cast it into an int
, because 33,4 becomes 33, and 33 points height label pass from two to one line!
Upvotes: 3
Reputation: 3784
The problem with
CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
is boundingRectWithSize
which determines the maximum value that CGRect can have.
My solution for this problem is to check if it exceeds, if not then text can fit into the label. I did it by using loops.
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
bool sizeFound = false;
while (!sizeFound) {
NSLog(@"Begin loop");
CGFloat fontSize = 14;
CGFloat previousSize = 0.0;
CGFloat currSize = 0.0;
for (float fSize = fontSize; fSize < fontSize+6; fSize++) {
CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fSize]}
context:nil];
currSize =r.size.width*r.size.height;
if (previousSize >= currSize) {
width = width*11/10;
height = height*11/10;
fSize = fontSize+10;
}
else {
previousSize = currSize;
}
NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
fSize,
r.size.width,
r.size.height,r.size.width*r.size.height);
}
if (previousSize == currSize) {
sizeFound = true;
}
}
NSLog(@"Size found with width %f and height %f", width, height);
After each iteration the size of height and width increments 10% of its value.
The reason why I picked 6 is because I did not want the label to be too squishy.
For a solution that does not use loops:
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
CGFloat currentFontSize = 12;
CGRect r1 = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
context:nil];
CGRect r2 = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
context:nil];
CGFloat firstVal =r1.size.width*r1.size.height;
CGFloat secondVal =r2.size.width*r2.size.height;
NSLog(@"First val %f and second val is %f", firstVal, secondVal);
if (secondVal > firstVal) {
float initRat = secondVal/firstVal;
float ratioToBeMult = sqrtf(initRat);
width *= ratioToBeMult;
height *= ratioToBeMult;
}
NSLog(@"Final width %f and height %f", width, height);
//for verifying
for (NSNumber *n in @[@(12.0f), @(14.0f), @(17.0f)]) {
CGFloat fontSize = [n floatValue];
CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
fontSize,
r.size.width,
r.size.height,r.size.width*r.size.height);
firstVal =r.size.width*r.size.height;
}
Where the last loop is proof that larger font can give a higher size result.
Upvotes: 2