我有一个UILabel
,其长度可能因iPhone或iPad上应用程序在纵向或横向模式下运行而异。当文本过长无法一行显示时,我希望用户能够按下它并获取弹出窗口中的完整文本。
如何检查UILabel
是否截断了文本?这是否可行?目前,我仅基于所处模式检查不同的长度,但这并不是很有效。
我有一个UILabel
,其长度可能因iPhone或iPad上应用程序在纵向或横向模式下运行而异。当文本过长无法一行显示时,我希望用户能够按下它并获取弹出窗口中的完整文本。
如何检查UILabel
是否截断了文本?这是否可行?目前,我仅基于所处模式检查不同的长度,但这并不是很有效。
label.bounds.size.width
NSString UIKit Additions有几种方法可以使用特定字体计算字符串的大小。然而,如果你的标签设置了最小字体大小,则系统允许缩小文本至该大小。在这种情况下,你可能需要使用sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:。CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
...
}
numberOfLines
返回的是用于显示文本的最大行数,详见 UILabel
类的参考文档:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UILabel_Class/index.html#//apple_ref/occ/instp/UILabel/numberOfLines - PaulSwift(作为扩展)- 适用于多行UILabel:
Swift 4:(boundingRect
的attributes
参数稍有变化)
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
}
}
编辑:我刚看到我的答案被点赞了,但是我给出的代码片段已经过时了。
现在最好的方法是(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");
}
看起来使用intrinsicContentSize
可以完成设置了attributedText
和text
的标签的工作。考虑到这一点,我认为我们可以安全地放弃所有边界框记录,并简化如下:
Swift 5.x
extension UILabel {
var isTruncated: Bool {
frame.width < intrinsicContentSize.width
}
var isClipped: Bool {
frame.height < intrinsicContentSize.height
}
}
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
}
}
你可以使用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;
}
textRectForBounds:limitedToNumberOfLines:
“您不应直接调用此方法”... - MartinsetNeedsLayout()
layoutIfNeeded()
- Jovan Jovanovski使用此类别查找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
补充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;
}
这就是它的作用。它可以与attributedText
一起使用,如果无法使用,则退回到普通的text
,对于那些处理多种字体族、大小甚至NSTextAttachments的人来说,这是很有意义的!
在自动布局中工作得很好,但显然必须在检查isTruncated
之前定义和设置约束,否则标签本身甚至不知道如何布局,因此它甚至不知道是否被截断。
只使用简单的NSString
和sizeThatFits
方法并不能解决这个问题。我不确定人们是如何获得积极结果的。顺便说一下,如前所述,使用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
}
}
以下是Swift 3的选定答案(作为扩展)。 OP在询问1行标签。 我尝试过的许多Swift答案都特定于多行标签,并且不能正确地标记单行标签。
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
}
}
intrinsicContentSize.width
对于文本和属性文本都适用吗? - Adrian
UILabel
API中。 - funct7