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

2019-02-17 12:12发布

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

enter image description here enter image description here

Example 2: the reduction in width leads to truncation

enter image description here enter image description here

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

enter image description here enter image description here

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

enter image description here enter image description here

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).

5条回答
冷血范
2楼-- · 2019-02-17 12:54

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楼-- · 2019-02-17 12:58

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楼-- · 2019-02-17 13:00

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楼-- · 2019-02-17 13:03

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

查看更多
SAY GOODBYE
6楼-- · 2019-02-17 13:13

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
查看更多
登录 后发表回答