I have a UILabel
that can be varying lengths depending on whether or not my app is running in portrait or landscape mode on an iPhone or iPad. When the text is
This works for iOS 8:
CGSize size = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size;
if (size.height > label.frame.size.height) {
NSLog(@"truncated");
}
EDIT: I just saw my answer was upvoted, but the code snippet I gave is deprecated.
Now the best way to do this is (ARC) :
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = mylabel.lineBreakMode;
NSDictionary *attributes = @{NSFontAttributeName : mylabel.font,
NSParagraphStyleAttributeName : paragraph};
CGSize constrainedSize = CGSizeMake(mylabel.bounds.size.width, NSIntegerMax);
CGRect rect = [mylabel.text boundingRectWithSize:constrainedSize
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes context:nil];
if (rect.size.height > mylabel.bounds.size.height) {
NSLog(@"TOO MUCH");
}
Note the calculated size is not integer value. So if you do things like int height = rect.size.height
, you will lose some floating point precision and may have wrong results.
Old answer (deprecated) :
If your label is multiline, you can use this code :
CGSize perfectSize = [mylabel.text sizeWithFont:mylabel.font constrainedToSize:CGSizeMake(mylabel.bounds.size.width, NSIntegerMax) lineBreakMode:mylabel.lineBreakMode];
if (perfectSize.height > mylabel.bounds.size.height) {
NSLog(@"TOO MUCH");
}
This is it. This works with attributedText
, before falling back to plain text
, which makes a lot of sense for us folks who deal with multiple font families, sizes, and even NSTextAttachments!
Works fine with autolayout, but obviously the constraints must be defined and set before we check isTruncated
, otherwise the label itself wont even know how to lay itself out, so no way it would even know if its truncated.
It doesnt work to approach this problem with just a plain NSString
and sizeThatFits
. Im not sure how people were getting positive results like that. BTW, as mentioned numerous times, using sizeThatFits
is not ideal at all because it takes into account numberOfLines
for the resulting size, which defeats the whole purpose of what we are trying to do, because isTruncated
would always return false
regardless if its truncated or not.
extension UILabel {
var isTruncated: Bool {
layoutIfNeeded()
let rectBounds = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
var fullTextHeight: CGFloat?
if attributedText != nil {
fullTextHeight = attributedText?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, context: nil).size.height
} else {
fullTextHeight = text?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size.height
}
return (fullTextHeight ?? 0) > bounds.size.height
}
}
Wouldnt it be easy to set the title attribute for the label , setting this will display full label when hovered.
you can calculate the length of the label and div width (convert to length - jQuery / Javascript - How do I convert a pixel value (20px) to a number value (20)).
set jquery to set title if length is greater than the div width.
var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}
Swift (as extension) - works for multi line uilabel:
swift4: (attributes
param of boundingRect
changed slightly)
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [.font: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
swift3:
extension UILabel {
var isTruncated: Bool {
guard let labelText = text else {
return false
}
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: [NSFontAttributeName: font],
context: nil).size
return labelTextSize.height > bounds.size.height
}
}
swift2:
extension UILabel {
func isTruncated() -> Bool {
if let string = self.text {
let size: CGSize = (string as NSString).boundingRectWithSize(
CGSize(width: self.frame.size.width, height: CGFloat(FLT_MAX)),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: self.font],
context: nil).size
if (size.height > self.bounds.size.height) {
return true
}
}
return false
}
}
Swift 3 solution
I think the best solution is to (1) create a UILabel
with the same properties as the label you're checking for truncation, (2) call .sizeToFit()
, (3) compare the attributes of the dummy label with your actual label.
For example, if you want to check whether a one lined label that has varying width truncates or not, then you can use this extension:
extension UILabel {
func isTruncated() -> Bool {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: CGFloat.greatestFiniteMagnitude, height: self.bounds.height))
label.numberOfLines = 1
label.font = self.font
label.text = self.text
label.sizeToFit()
if label.frame.width > self.frame.width {
return true
} else {
return false
}
}
}
...but again, you can easily modify the above code to fit your needs. So let's say your label is multilined and has varying height. Then the extension would look something like this:
extension UILabel {
func isTruncated() -> Bool {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.font = self.font
label.text = self.text
label.sizeToFit()
if label.frame.height > self.frame.height {
return true
} else {
return false
}
}
}