如何检查 UILabel 是否被截断?

我有一个 UILabel,它可以根据我的应用程序是否在 iPhone 或 iPad 上以纵向或横向模式运行而变化长度。当文本太长,显示在一行和它截断我希望用户能够按下它,并得到一个全文弹出。

如何检查 UILabel是否截断了文本?这有可能吗?现在我只是检查不同的长度,根据我在什么模式,但它不工作超级好。

66806 次浏览

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

NSString UIKit Adtions 有几种方法用于计算具有特定字体的字符串的大小。但是,如果您的标签有一个最小的 FontSize,允许系统将文本缩小到该大小。在这种情况下,您可能需要使用 SizeWithFont: minFontSize: realalFontSize: forWidth: lineBreakMode:

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
...
}

您可以使用 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;
}

编辑: 我刚刚看到我的答案被否决了,但是我给出的代码片段被否决了。
现在最好的方法是(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这样的事情,你将失去一些浮点精度,并可能有错误的结果。

旧答案 (弃用) :

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");
}

因为上面的所有答案都使用了折旧法,我想这个可能会有用:

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

使用此类别查找 iOS7及以上版本的标签是否被截断。

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

为了补充 @ 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;
}

为了增加 IDev的答案,您应该使用 intrinsicContentSize而不是 frame,使其适用于自动布局

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

这对于 iOS8来说是可行的:

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");
}

为了处理 iOS6(是的,我们中的一些人仍然不得不这样做) ,这里有@iDev 的答案的另一个扩展。关键是,对于 iOS6,在调用 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);
}

我已经写了一个类别来处理 UILabel 的截断。可以在 iOS7和更高版本中使用。希望对你有所帮助! 尾部截断,尾部截断

@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

Swift (作为扩展)-适用于多行 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
}
}

雨燕3:

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

迅速2:

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
}


}

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

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

如果长度大于 div 宽度,则设置 jquery 来设置 title。

var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}

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

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

Swift 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 {
guard numberOfLines > 0 else { return false }
return countLabelLines() > numberOfLines
}
}

以下是 Swift 3中的选定答案(作为扩展)。OP 询问关于一行标签。我在这里尝试的许多快速答案都是针对多行标签的,并没有在单行标签上正确标记。

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

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

在使用自动布局(设置最大高度)和 NSParagraph.lineSpacing的属性文本时,我遇到了 boundingRect(with:options:attributes:context:)的问题

行之间的间距被忽略(即使在 attributes中传递给 boundingRect方法时也是如此) ,因此标签在被截断时可能被认为没有被截断。

我发现的解决方案是使用 UIView.sizeThatFits:

extension UILabel {
var isTruncated: Bool {
layoutIfNeeded()
let heightThatFits = sizeThatFits(bounds.size).height
return heightThatFits > bounds.size.height
}
}

就是这个。这适用于 attributedText,然后回到简单的 text,这对于我们这些处理多种字体系列、大小,甚至是 NSTextAttachments 的人来说非常有意义!

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

只用一个简单的 NSStringsizeThatFits解决这个问题是行不通的。我不知道人们是如何得到这样积极的结果。顺便说一句,正如我们多次提到的,使用 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
}
}

确保在 viewDidLayoutSubviews 中调用其中的任何一个。

public extension UILabel {


var isTextTruncated: Bool {
layoutIfNeeded()
return text?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil).size.height ?? 0 > bounds.size.height
}


var isAttributedTextTruncated: Bool {
layoutIfNeeded()
return attributedText?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height ?? 0 > bounds.size.height
}


}

SWIFT 5

Example for a multiple lined UILabel that is set to display only 3 lines.

    let labelSize: CGSize = myLabel.text!.size(withAttributes: [.font: UIFont.systemFont(ofSize: 14, weight: .regular)])


if labelSize.width > myLabel.intrinsicContentSize.width * 3 {
// your label will truncate
}

虽然用户可以选择返回键添加一个额外的行而不增加“文本宽度”在这种情况下,这样的东西也可能是有用的。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {


if text == "\n" {
// return pressed


}
}

在 Swift 5. x 中

let size = label.text?.size(withAttributes: [NSAttributedString.Key.font: label.font!])
if size!.width > label.bounds.size.width {
debugPrint("Size increased", size?.width ?? 0, label.bounds.size.width, label.text ?? "")
}

看来 intrinsicContentSize将得到与 attributedTexttext文本设置标签的工作完成。考虑到这一点,我认为我们可以放心地放弃所有的边框簿记,简化如下:

斯威夫特5. x

extension UILabel {
var isTruncated: Bool {
frame.width < intrinsicContentSize.width
}


var isClipped: Bool {
frame.height < intrinsicContentSize.height
}
}