ios - 如何检查UILabel是否被截断?

我有一个UILabel,根据我的应用程序是在iPhone或iPad上以纵向还是横向模式运行,可以有不同的长度。 当文本太长而无法在一行显示并且它被截断时,我希望用户能够按下它并获得全文的弹出窗口。

如何查看UILabel是否截断文本? 它甚至可能吗? 现在我只是根据我所处的模式检查不同长度,但它不能很好地工作。

Randall asked 2019-09-10T23:47:00Z
18个解决方案
95 votes

您可以计算字符串的宽度,并查看宽度是否大于label.bounds.size.width

NSString UIKit Additions有几种方法可以用特定字体计算字符串的大小。 但是,如果您的标签有minimumFontSize,则允许系统将文本缩小到该大小。 您可能想要使用sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:在这种情况下。

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
   ...
}
progrmr answered 2019-09-10T23:47:21Z
72 votes

Swift(作为扩展) - 适用于多行uilabel:

swift4:(attributes param of boundingRect略有变化)

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
    }

}
Robin answered 2019-09-10T23:48:03Z
19 votes

编辑:我刚刚看到我的回答被upvoted,但我给出的代码片段已被弃用。
现在最好的方法是(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");
}

请注意,计算的大小不是整数值。 因此,如果您执行int height = rect.size.height之类的操作,则会丢失一些浮点精度,并且可能会出现错误的结果。

旧答案(不建议使用):

如果您的标签是多行的,则可以使用以下代码:

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");
}
Martin answered 2019-09-10T23:48:54Z
12 votes

你可以用UILabel制作一个类别

- (BOOL)isTextTruncated

{
    CGRect testBounds = self.bounds;
    testBounds.size.height = NSIntegerMax;
    CGRect limitActual = [self textRectForBounds:[self bounds] limitedToNumberOfLines:self.numberOfLines];
    CGRect limitTest = [self textRectForBounds:testBounds limitedToNumberOfLines:self.numberOfLines + 1];
    return limitTest.size.height>limitActual.size.height;
}
DongXu answered 2019-09-10T23:49:18Z
9 votes

使用此类别查找是否在iOS 7及更高版本上截断标签。

// UILabel+Truncation.h
@interface UILabel (Truncation)

@property (nonatomic, readonly) BOOL isTruncated;

@end


// UILabel+Truncation.m
@implementation UILabel (Truncation)

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

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

@end
Rajesh answered 2019-09-10T23:49:42Z
8 votes

要添加到iDev的答案,您应该使用intrinsicContentSize而不是frame,以使其适用于Autolayout

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

        if (self.intrinsicContentSize.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}
onmyway133 answered 2019-09-10T23:50:08Z
7 votes

斯威夫特3

您可以在分配字符串后计算行数,并与标签的最大行数进行比较。

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
    }
}
Claus answered 2019-09-10T23:50:35Z
2 votes

这适用于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");
}
Marcio Fonseca answered 2019-09-10T23:51:00Z
2 votes

我写了一个用于处理UILabel截断的类别。 适用于iOS 7及更高版本。 希望能帮助到你 !uilabel尾截断

@implementation UILabel (Truncation)

- (NSRange)truncatedRange
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];

    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];

    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
    textContainer.lineFragmentPadding = 0;
    [layoutManager addTextContainer:textContainer];

    NSRange truncatedrange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:0];
    return truncatedrange;
}

- (BOOL)isTruncated
{
    return [self truncatedRange].location != NSNotFound;
}

- (NSString *)truncatedText
{
    NSRange truncatedrange = [self truncatedRange];
    if (truncatedrange.location != NSNotFound)
    {
        return [self.text substringWithRange:truncatedrange];
    }

    return nil;
}

@end
Alejandro Cotilla answered 2019-09-10T23:51:25Z
1 votes

为了添加到@iDev所做的,我修改了self.frame.size.height以使用label.frame.size.height并且也没有使用NSStringDrawingUsesLineFontLeading。在这些修改之后,我实现了截断发生的完美计算(至少在我的情况下)。

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

    if (label.frame.size.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}
kgaidis answered 2019-09-10T23:51:51Z
1 votes

这是Swift 3中的选定答案(作为扩展名)。 OP询问了1行标签。 我在这里尝试的许多快速答案都特定于多行标签,并且在单行标签上没有正确标记。

extension UILabel {
    var isTruncated: Bool {
        guard let labelText = text as? NSString else {
            return false
        }
        let size = labelText.size(attributes: [NSFontAttributeName: font])
        return size.width > self.bounds.width
    }
}
Travis M. answered 2019-09-10T23:52:17Z
1 votes
extension UILabel {

public func resizeIfNeeded() -> CGFloat? {
    guard let text = text, !text.isEmpty else { return nil }

    if isTruncated() {
        numberOfLines = 0
        sizeToFit()
        return frame.height
    }
    return nil
}

func isTruncated() -> Bool {
    guard let text = text, !text.isEmpty else { return false }

    let size: CGSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
    return size.width > self.bounds.size.width
    }
}

您可以计算字符串的宽度,并查看宽度是否大于标签宽度。

Hayk Brsoyan answered 2019-09-10T23:52:44Z
0 votes

因为上面的所有答案都使用了弃用的方法,我认为这可能很有用:

- (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;
}
Raz answered 2019-09-10T23:53:10Z
0 votes

为了处理iOS 6(是的,我们中的一些人仍然需要),这是@ iDev的答案的另一个扩展。 关键的一点是,对于iOS 6,要确保在调用sizeThatFits之前将UILabel的numberOfLines设置为0; 如果没有,它会给你一个结果,上面写着“绘制numberOfLines值高度的点数”来绘制标签文字。

- (BOOL)isTruncated
{
    CGSize sizeOfText;

    // iOS 7 & 8
    if([self.text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
    {
        sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)
                                             options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                          attributes:@{NSFontAttributeName:self.font}
                                             context:nil].size;
    }
    // iOS 6
    else
    {
        // For iOS6, set numberOfLines to 0 (i.e. draw label text using as many lines as it takes)
        //  so that siteThatFits works correctly. If we leave it = 1 (for example), it'll come
        //  back telling us that we only need 1 line!
        NSInteger origNumLines = self.numberOfLines;
        self.numberOfLines = 0;
        sizeOfText = [self sizeThatFits:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)];
        self.numberOfLines = origNumLines;
    }

    return ((self.bounds.size.height < sizeOfText.height) ? YES : NO);
}
John Jacecko answered 2019-09-10T23:53:36Z
0 votes

使用autolayout(设置最大高度)和UIView.sizeThatFits的属性文本时,我遇到了boundingRect(with:options:attributes:context:)的问题

行之间的间距被忽略(即使在UIView.sizeThatFits中传递给boundingRect方法时),因此标签可能被视为未被截断。

我找到的解决方案是使用UIView.sizeThatFits

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()
        let heightThatFits = sizeThatFits(bounds.size).height
        return heightThatFits > bounds.size.height
    }
}
Axel Guilmin answered 2019-09-10T23:54:16Z
0 votes

这是男生。 这适用于attributedText,然后回到平原NSString,这对我们处理多种字体系列,大小甚至NSTextAttachments的人来说很有意义!

使用自动布局可以正常工作,但显然必须在我们检查NSString之前定义和设置约束,否则标签本身甚至不知道如何自行布局,所以它甚至不知道它是否被截断。

它只是一个简单的NSStringsizeThatFits无法解决这个问题。我不确定人们是如何得到这样的积极结果。 BTW,正如无数次提到的那样,使用sizeThatFits并不理想,因为它考虑了numberOfLines的结果大小,这违背了我们正在尝试做的全部目的,因为isTruncated总是会返回false,无论它是否被截断。

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
    }
}
Lucas Chwe answered 2019-09-10T23:54:58Z
-1 votes

Swift 3解决方案

我认为最好的解决方案是(1)创建一个UILabel,其属性与您要检查截断的标签相同,(2)调用.sizeToFit(),(3)比较虚拟标签的属性与实际标签。

例如,如果要检查具有不同宽度的一个带衬里标签是否截断,则可以使用此扩展名:

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
        }
    }
}

...但是,您可以轻松修改上述代码以满足您的需求。 因此,假设您的标签是多线的,并且具有不同的高度。 然后扩展名看起来像这样:

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
        }
    }
}
Saoud Rizwan answered 2019-09-10T23:55:47Z
-6 votes

不是很容易为标签设置title属性,设置这将在悬停时显示完整标签。

你可以计算标签的长度和div宽度(转换为长度 - jQuery / Javascript - 如何将像素值(20px)转换为数值(20))。

如果length大于div宽度,则设置jquery设置标题。

var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}
user3405326 answered 2019-09-10T23:56:29Z
translate from https://stackoverflow.com:/questions/3077109/how-to-check-if-uilabel-is-truncated