Как вычислить высоту UILabel динамически?
Я хочу вычислить количество строк и высоту UILabel динамически из данного текста для того же.
12 ответов:
попробуй такое
// 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
или
int lines = labelHeight/16; NSLog(@"lines count : %i \n\n",lines);
или
int lines = [myLabel.text sizeWithFont:myLabel.font constrainedToSize:myLabel.frame.size lineBreakMode:UILineBreakModeWordWrap].height /myLabel.font.pointSize; //fetching font size from font
используя категории, просто создайте класс категории с именем
UILabel+UILabelDynamicHeight.h
UILabel+UILabelDynamicHeight.м
нет больше напряжения о вычислении высоты. Пожалуйста, ознакомьтесь с приведенной ниже реализацией.
обновления для iOS7 и выше, iOS 7 ниже: динамический расчет Высота 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 <UIKit/UIKit.h> @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.м
#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
вызов
-sizeToFit
на экземпляре UILabel будет автоматически изменять его размер, чтобы соответствовать тексту, который он отображает, не требуется вычисление. Если вам нужен размер, вы можете получить его из свойства рамки метки после этого.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));
подводя итог, вы можете рассчитать высоту метки, используя ее строку и вызывая
boundingRectWithSize
. Вы должны предоставитьfont
как атрибут, и включить.usesLineFragmentOrigin
для многострочных меток.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
некоторые расширения, чтобы сделать это:
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 } }
Objective-C Version:
Утилита UILabel+.h
#import <UIKit/UIKit.h> @interface UILabel (Utility) - (CGFloat)textHeightForWidth:(CGFloat)width; - (CGFloat)attributedTextHeightForWidth:(CGFloat)width; @end
Утилита UILabel+.м
@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
текущее решение является устаревшим, начиная с версии iOS 7.
вот обновленное решение:
+ (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; }
причина, по которой мое решение настроено таким образом, заключается в том, что я использую UITableViewCell и динамически изменяю размер ячейки относительно того, сколько места займет метка.
без вызова sizeToFit, вы можете сделать это все численно с очень подключи и играй решение:
+ (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; }
Я использую его много для UITableViewCells, которые имеют динамически выделенные высоты.
решает проблему атрибутов, а также @Salman Zaidi.
скопируйте и вставьте этот метод и используйте его как:
[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 чтобы получить высоту для NSAttributedString используйте эту функцию ниже. Где ширина - ширина UILabel или 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 }
чтобы получить высоту для строка используйте эту функцию, она почти идентична предыдущему методу:
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 }
в моем случае, я использовал заголовок фиксированного размера для каждого раздела, но с динамически размер ячейки в каждом заголовке. Высота ячейки зависит от высоты метки.
работа с:
tableView.estimatedRowHeight = SomeNumber tableView.rowHeight = UITableViewAutomaticDimension
работает но при использовании:
tableView.reloadSections(IndexSet(integer: sender.tag) , with: .automatic)
когда много заголовков не свернуты, создает много ошибок, таких как дублирование заголовка (тип заголовка x ниже того же типа) и странные анимации, когда структура перезагружается с анимацией, даже при использовании с типом
.none
(FYI, фиксированная высота заголовка и высота ячейки работает).решение делает использование
heightForRowAt
обратный вызов и вычислить высоту метки самостоятельно (плюс анимация выглядит большое лучше). Помните, что высота называется первый.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 }
Это расширение, которое я использую для вычисления многострочных высот UILabel, это скорректированный фрагмент из предыдущего сообщения переполнения стека:
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) } }
вам нужно создать расширение строки и вызвать этот метод
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) }
вы должны отправить ширину метку