¿Cómo verificar si UILabel está truncado?

Tengo un UILabel que puede variar en función de si mi aplicación se está ejecutando en modo retrato o paisaje en un iPhone o iPad. Cuando el texto es demasiado largo para mostrarse en una línea y se trunca, quiero que el usuario pueda presionarlo y obtener una ventana emergente del texto completo.

¿Cómo puedo verificar si UILabel está truncando el texto? ¿Es posible? En este momento solo estoy buscando diferentes longitudes según el modo en el que estoy pero no funciona súper bien.

Puede calcular el ancho de la cadena y ver si el ancho es mayor que label.bounds.size.width

NSString UIKit Additions tiene varios métodos para calcular el tamaño de la cadena con una fuente específica. Sin embargo, si tiene un MinimumFontSize para su etiqueta, esto le permite al sistema reducir el texto a ese tamaño. Puede usar sizeWithFont: minFontSize: actualFontSize: forWidth: lineBreakMode: en ese caso.

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

Swift (como extensión): funciona para uilabel de varias líneas:

swift4: (los attributes param de boundingRect cambiaron ligeramente)

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

EDITAR: Acabo de ver que mi respuesta fue aprobada, pero el fragmento de código que di está en desuso.
Ahora la mejor manera de hacerlo es (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"); } 

Tenga en cuenta que el tamaño calculado no es un valor entero. Entonces, si haces cosas como int height = rect.size.height , perderás una precisión de coma flotante y es posible que obtengas resultados incorrectos.

Respuesta anterior (obsoleta):

Si su etiqueta es multilínea, puede usar este código:

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

puedes hacer una categoría con 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; } 

Para agregar a la respuesta de iDev , debe usar intrinsicContentSize lugar de frame , para que funcione en 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; } 

Utilice esta categoría para encontrar si una etiqueta está truncada en iOS 7 o superior.

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

Swift 3

Puede contar el número de líneas después de asignar la cadena y compararla con el número máximo de líneas de la etiqueta.

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

He escrito una categoría para trabajar con el truncamiento de UILabel. Funciona en iOS 7 y versiones posteriores. Espero eso ayude ! truncamiento de la cola de 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 

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

Para agregar a lo que hizo @iDev , modifiqué self.frame.size.height para usar label.frame.size.height y tampoco NSStringDrawingUsesLineFontLeading . Después de esas modificaciones, logré calcular perfectamente cuándo ocurriría el truncamiento (al menos en mi caso).

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

Aquí está la respuesta seleccionada en Swift 3 (como una extensión). El OP estaba preguntando por 1 línea de tags. Muchas de las respuestas rápidas que intenté aquí son específicas para tags de varias líneas y no están marcando correctamente en tags de una sola línea.

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

Puede calcular el ancho de la cadena y ver si el ancho es mayor que el ancho de la etiqueta.

Debido a que todas las respuestas anteriores usan métodos depreciados, pensé que esto podría ser útil:

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

Para manejar iOS 6 (sí, algunos de nosotros todavía tenemos que hacerlo), aquí hay otra expansión a la respuesta de @ iDev. El punto clave es que, para iOS 6, para asegurarse de que el número de líneas de UILabel esté establecido en 0 antes de llamar a sizeThatFits; si no, le dará un resultado que dice “los puntos para dibujar numberOfLines worth of height” son necesarios para dibujar el texto de la etiqueta.

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

Tuve problemas con boundingRect(with:options:attributes:context:) al usar autolayout (para establecer una altura máxima) y un texto atribuido con NSParagraph.lineSpacing

El espaciado entre líneas se ignoró (incluso cuando se pasaban attributes al método boundingRect ) por lo que la etiqueta podría considerarse no truncada cuando lo era.

La solución que encontré es usar UIView.sizeThatFits :

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

Solución Swift 3

Creo que la mejor solución es (1) crear un UILabel con las mismas propiedades que la etiqueta que está buscando para el truncamiento, (2) llamar .sizeToFit() , (3) comparar los atributos de la etiqueta ficticia con su etiqueta actual .

Por ejemplo, si desea verificar si una etiqueta alineada que tiene ancho variable trunca o no, puede usar esta extensión:

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

… pero de nuevo, puede modificar fácilmente el código anterior para que se ajuste a sus necesidades. Digamos que su etiqueta es multilínea y tiene una altura variable. Entonces la extensión se vería así:

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

¿No sería fácil establecer el atributo de título para la etiqueta, estableciendo que esto mostrará la etiqueta completa cuando esté suspendido?

puede calcular la longitud de la etiqueta y el ancho div (convertir a longitud – jQuery / Javascript – Cómo convierto un valor de píxel (20px) a un valor numérico (20) ).

establezca jquery para establecer el título si la longitud es mayor que el ancho del div.

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