How to tell when a UILabel will be truncated an/or

2019-02-17 12:39发布

问题:

I have a multi-line UILabel (numberOfLines = 0). It's width can change at runtime, and sometimes this leads to truncation and/or re-wrapping. Some examples illustrate this best:

Example 1: the reduction in width leads to a different line break point

Example 2: the reduction in width leads to truncation

Example 3: the reduction in width leads to both truncation and a different line break position

Example 4: the reduction in width does not have any effect on truncation or line break position

Since this change in formatting can be quite jarring, I intend to mask it behind some animation (probably a fade in/fade out). However, the first hurdle is identifying when I need to do this. I don't want to apply the animation whenever the label re-sizes - only when it will cause a change in either truncation or line break positions.

How might I test this? The test should return YES for example 1, 2, and 3, but NO for example 4.

Note: the resizing will never alter the number of lines in my example.

Note 2: if anyone has some better tags related to text formatting I'd love to know them - feel free to edit.

Note 3: if you are interested in seeing this behavior accomplished, try Apple's mail.app on the iPhone. When viewing the inbox, swipe an email and watch the summary line fade-in/out as it re-wraps and/or truncates (but not when it doesn't need to).

回答1:

You could know the size of label that is needed to display a particular NSString instance. For example, you could use that one:

- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode

Returns the size of the string if it were rendered with the specified constraints.

So what you want is to get CGSize for a particular string and check if it is not larger then UILabel size:

    UILabel *label;
    CGSize sizeNeeded = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(label.bounds.size.width, MAXFLOAT)];
    if (sizeNeeded.height > label.bounds.size.height)
    {
        NSLog(@"String is truncated");
    }

More useful NSString methods you could find here: NSString UIKit Additions Reference

Ok, another way of doing what you want:

1) Create 2 UILabel with the same properties but second one (label2) will be with another one width.

2) Set alpha property of label2 to 0.0 in non-edit mode.

3) When edit mode begins make such animation:

// label1.alpha == 1.0, label2.alpha == 0.0 
{[UIView animateWithDuration:0.5 animations:^{
    label1.alpha = 0.0;
    label2.alpha = 1.0;
}];

4) When edit mode ends:

{[UIView animateWithDuration:0.5 animations:^{
    label1.alpha = 1.0;
    label2.alpha = 0.0;
}];

That will give you the same result as in Mail.app



回答2:

Swift 3 solution

You can count the number of lines after assigning the string and compare to the max number of lines of the label.

import Foundation
import UIKit

extension UILabel {

    func countLabelLines() -> Int {
        // Call self.layoutIfNeeded() if your view is uses auto layout
        let myText = self.text! as NSString
        let attributes = [NSFontAttributeName : self.font]

        let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil)
        return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight))
    }

    func isTruncated() -> Bool {

        if (self.countLabelLines() > self.numberOfLines) {
            return true
        }
        return false
    }
}


回答3:

The answer above is using a depreciated method, so i thought the following code could be useful:

- (BOOL)isLabelTruncated:(UILabel *)label
{
    BOOL isTruncated = NO;

    CGRect labelSize = [label.text boundingRectWithSize:CGSizeFromString(label.text) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil];

    if (labelSize.size.width / labelSize.size.height > label.numberOfLines) {

        isTruncated = YES;
    }

    return isTruncated;
}


回答4:

Use this method to find lable truncated in iOS 7.

- (BOOL)isTruncated:(UILabel *)label{
        CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.bounds.size.width, CGFLOAT_MAX)
                                                     options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                  attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;

        if (self.frame.size.height < ceilf(sizeOfText.height)) {
            return YES;
        }
        return NO;
    }


回答5:

For versions higher than iOS 7, you can check the following solutions:

  1. https://stackoverflow.com/a/30813691/2123122
  2. https://stackoverflow.com/a/32094824/2123122