¿Cómo calcular la altura de UILabel de forma dinámica?

Quiero calcular el número de líneas y la altura de UILabel de forma dinámica a partir de un texto dado.

Prueba esto

// UILabel *myLabel; CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font constrainedToSize:myLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping]; CGFloat labelHeight = labelSize.height; int lines = [myLabel.text sizeWithFont:myLabel.font constrainedToSize:myLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping].height/16; // '16' is font size 

o

 int lines = labelHeight/16; NSLog(@"lines count : %i \n\n",lines); 

o

 int lines = [myLabel.text sizeWithFont:myLabel.font constrainedToSize:myLabel.frame.size lineBreakMode:UILineBreakModeWordWrap].height /myLabel.font.pointSize; //fetching font size from font 

Al usar categorías, solo crea la clase de categoría nombrada como

UILabel + UILabelDynamicHeight.h

UILabel + UILabelDynamicHeight.m

No más tensión sobre el cálculo de altura. Por favor revise la implementación a continuación.

Actualizaciones para iOS7 y superiores, iOS 7 a continuación: calcular dinámicamente la altura de UILabel

 #define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame) #define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending) #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) #define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending) #define iOS7_0 @"7.0" 

UILabel + UILabelDynamicHeight.h

 #import  @interface UILabel (UILabelDynamicHeight) #pragma mark - Calculate the size the Multi line Label /*====================================================================*/ /* Calculate the size of the Multi line Label */ /*====================================================================*/ /** * Returns the size of the Label * * @param aLabel To be used to calculte the height * * @return size of the Label */ -(CGSize)sizeOfMultiLineLabel; @end 

UILabel + UILabelDynamicHeight.m

 #import "UILabel+UILabelDynamicHeight.h" @implementation UILabel (UILabelDynamicHeight) #pragma mark - Calculate the size,bounds,frame of the Multi line Label /*====================================================================*/ /* Calculate the size,bounds,frame of the Multi line Label */ /*====================================================================*/ /** * Returns the size of the Label * * @param aLabel To be used to calculte the height * * @return size of the Label */ -(CGSize)sizeOfMultiLineLabel{ //Label text NSString *aLabelTextString = [self text]; //Label font UIFont *aLabelFont = [self font]; //Width of the Label CGFloat aLabelSizeWidth = self.frame.size.width; if (SYSTEM_VERSION_LESS_THAN(iOS7_0)) { //version < 7.0 return [aLabelTextString sizeWithFont:aLabelFont constrainedToSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping]; } else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(iOS7_0)) { //version >= 7.0 //Return the calculated size of the Label return [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : aLabelFont } context:nil].size; } return [self bounds].size; } @end 

Al llamar a -sizeToFit en la instancia de UILabel, se redimensionará automáticamente para que se ajuste al texto que muestra, sin necesidad de cálculos. Si necesita el tamaño, puede obtenerlo de la propiedad del marco de la etiqueta después de eso.

 label.numberOfLines = 0; // allows label to have as many lines as needed label.text = @"some long text"; [label sizeToFit]; NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame)); 

Para resumir, puede calcular el alto de una etiqueta utilizando su cadena y llamando a boundingRectWithSize . Debe proporcionar la font como un atributo e incluir .usesLineFragmentOrigin para .usesLineFragmentOrigin de varias líneas.

 let labelWidth = label.frame.width let maxLabelSize = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude) let actualLabelSize = label.text!.boundingRect(with: maxLabelSize, options: [.usesLineFragmentOrigin], attributes: [.font: label.font], context: nil) let labelHeight = actualLabelSize.height 

Algunas extensiones para hacer justamente eso:

Versión Swift:

 extension UILabel { func textHeight(withWidth width: CGFloat) -> CGFloat { guard let text = text else { return 0 } return text.height(withWidth: width, font: font) } func attributedTextHeight(withWidth width: CGFloat) -> CGFloat { guard let attributedText = attributedText else { return 0 } return attributedText.height(withWidth: width) } } extension String { func height(withWidth width: CGFloat, font: UIFont) -> CGFloat { let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let actualSize = self.boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], attributes: [.font : font], context: nil) return actualSize.height } } extension NSAttributedString { func height(withWidth width: CGFloat) -> CGFloat { let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let actualSize = boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], context: nil) return actualSize.height } } 

Versión de Objective-C:

UILabel + Utility.h

 #import  @interface UILabel (Utility) - (CGFloat)textHeightForWidth:(CGFloat)width; - (CGFloat)attributedTextHeightForWidth:(CGFloat)width; @end 

UILabel + Utility.m

 @implementation NSString (Utility) - (CGFloat)heightForWidth:(CGFloat)width font:(UIFont *)font { CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX); CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size; return actualSize.height; } @end @implementation NSAttributedString (Utility) - (CGFloat)heightForWidth:(CGFloat)width { CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX); CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin context:nil].size; return actualSize.height; } @end @implementation UILabel (Utility) - (CGFloat)textHeightForWidth:(CGFloat)width { return [self.text heightForWidth:width font:self.font]; } - (CGFloat)attributedTextHeightForWidth:(CGFloat)width { return [self.attributedText heightForWidth:width]; } @end 

La solución actual ha quedado obsoleta a partir de iOS 7.

Aquí hay una solución actualizada:

 + (CGFloat)heightOfCellWithIngredientLine:(NSString *)ingredientLine withSuperviewWidth:(CGFloat)superviewWidth { CGFloat labelWidth = superviewWidth - 30.0f; // use the known label width with a maximum height of 100 points CGSize labelContraints = CGSizeMake(labelWidth, 100.0f); NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init]; CGRect labelRect = [ingredientLine boundingRectWithSize:labelContraints options:NSStringDrawingUsesLineFragmentOrigin attributes:nil context:context]; // return the calculated required height of the cell considering the label return labelRect.size.height; } 

La razón por la que mi solución está configurada así es porque estoy usando una UITableViewCell y redimensionando la celda de forma dinámica en relación con la cantidad de espacio que ocupará la etiqueta.

Sin llamar a sizeToFit, puede hacer esto de forma numérica con una solución muy plug and play:

 + (CGFloat)heightForText:(NSString*)text font:(UIFont*)font withinWidth:(CGFloat)width { CGSize size = [text sizeWithAttributes:@{NSFontAttributeName:font}]; CGFloat area = size.height * size.width; CGFloat height = roundf(area / width); return ceilf(height / font.lineHeight) * font.lineHeight; } 

Lo uso mucho para UITableViewCells que tienen alturas dinámicamente asignadas.

Resuelve el problema de atributos también @Salman Zaidi.

Copie y pegue este método y lo utilizó como:

 [lblText setFrame:CGRectMake(lblText.frame.origin.x, lblText.frame.origin.y, width, [self getLabelHeight:lblText])]; - (CGFloat)getLabelHeight:(UILabel*)label { CGSize constraint = CGSizeMake(label.frame.size.width, CGFLOAT_MAX); CGSize size; NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init]; CGSize boundingBox = [label.text boundingRectWithSize:constraint options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:context].size; size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height)); return size.height; } 
 CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX); CGSize requiredSize = [lbl sizeThatFits:maxSize]; CGFloat height=requiredSize.height 

Swift 4 Para obtener la altura para NSAttributedString, use esta función a continuación. Where width – el ancho de su UILabel o UITextView

 func getHeight(for attributedString: NSAttributedString, font: UIFont, width: CGFloat) -> CGFloat { let textStorage = NSTextStorage(attributedString: attributedString) let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)) let layoutManager = NSLayoutManager() layoutManager.addTextContainer(textContainter) textStorage.addLayoutManager(layoutManager) textStorage.addAttribute(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, textStorage.length)) textContainter.lineFragmentPadding = 0.0 layoutManager.glyphRange(for: textContainter) return layoutManager.usedRect(for: textContainter).size.height } 

Para obtener la altura de String, use esta función. Es casi idéntico al método anterior:

 func getHeight(for string: String, font: UIFont, width: CGFloat) -> CGFloat { let textStorage = NSTextStorage(string: string) let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)) let layoutManager = NSLayoutManager() layoutManager.addTextContainer(textContainter) textStorage.addLayoutManager(layoutManager) textStorage.addAttribute(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, textStorage.length)) textContainter.lineFragmentPadding = 0.0 layoutManager.glyphRange(for: textContainter) return layoutManager.usedRect(for: textContainter).size.height } 

En mi caso, estaba usando un encabezado de tamaño fijo para cada sección pero con un tamaño de celda dynamic en cada encabezado. La altura de la celda depende de la altura de la etiqueta.

Trabajando con:

 tableView.estimatedRowHeight = SomeNumber tableView.rowHeight = UITableViewAutomaticDimension 

Funciona pero al usar:

 tableView.reloadSections(IndexSet(integer: sender.tag) , with: .automatic) 

cuando muchos encabezados no se colapsan, crea una gran cantidad de errores como duplicación de encabezado (tipo de encabezado x debajo del mismo tipo) y animaciones extrañas cuando el marco se recarga con animación, incluso cuando se usa con tipo .none (FYI, un encabezado fijo altura y altura de celda funciona).

La solución es hacer el uso de la callback heightForRowAt y calcular la altura de la etiqueta por su cuenta (más la animación se ve mucho mejor). Recuerde que la altura se llama primero .

 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let object = dataDetailsController.getRowObject(forIndexPath: indexPath) let label = UILabel(frame: tableView.frame) let font = UIFont(name: "HelveticaNeue-Bold", size: 25) label.text = object?.name label.font = font label.numberOfLines = 0 label.textAlignment = .center label.sizeToFit() let size = label.frame.height return Float(size) == 0 ? 34 : size } 

Esta es la extensión que uso para calcular alturas UILabel de líneas múltiples, es un fragmento ajustado de una publicación previa de desbordamiento de stack:

 extension UILabel { func estimatedHeight(forWidth: CGFloat, text: String, ofSize: CGFloat) -> CGFloat { let size = CGSize(width: forWidth, height: CGFloat(MAXFLOAT)) let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin) let attributes = [NSFontAttributeName: UIFont.systemFont(ofSize: ofSize)] let rectangleHeight = String(text).boundingRect(with: size, options: options, attributes: attributes, context: nil).height return ceil(rectangleHeight) } } 

Necesita crear una extensión de String y llamar a este método

 func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil) return ceil(boundingBox.height) } 

Debe enviar el ancho de su etiqueta

si desea que la etiqueta tome líneas dinámicas, puede usar esto

 label.numberOfLines = 0; // allows label to have as many lines as needed label.text = @"some long text "; [label sizeToFit]; NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame));