Я хочу рассчитать количество строк и высоту UILabel динамически из заданного текста для этого.
Как вычислить высоту UILabel динамически?
Ответ 1
Попробуйте это
// 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.m
Больше нет напряжения в расчете высоты. Просмотрите приведенную ниже информацию.
Обновления для 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.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
Ответ 2
Вызов -sizeToFit
в экземпляре UILabel автоматически изменит его размер, чтобы он соответствовал отображаемому тексту, без необходимости вычисления. Если вам нужен размер, вы можете получить его из свойства рамки метки после этого.
label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text";
[label sizeToFit];
NSLog(@"Label frame is: %@", NSStringFromCGRect(label.frame));
Ответ 3
Подводя итог, вы можете вычислить высоту метки, используя ее строку и вызывая 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(withWidth:labelWidth)
Некоторые расширения для этого:
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:
UILabel + Utility.h
#import <UIKit/UIKit.h>
@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
Ответ 4
Текущее решение устарело с 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 и динамически изменяю размер ячейки относительно того, сколько места займет эта метка.
Ответ 5
Без вызова 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.
Ответ 6
Скопируйте и вставьте этот метод и используйте его как:
[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;
}
Ответ 7
CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX);
CGSize requiredSize = [lbl sizeThatFits:maxSize];
CGFloat height=requiredSize.height
Ответ 8
Вам нужно создать расширение String и вызвать этот метод
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)
}
Вы должны отправить ширину своей метки
Ответ 9
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
}
Чтобы получить высоту для String, используйте эту функцию, она практически идентична предыдущему методу:
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
}
Ответ 10
В моем случае я использовал заголовок фиксированного размера для каждого раздела, но с динамическим размером в каждом заголовке. Высота ячейки зависит от высоты надписи.
Работа с:
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
}
Ответ 11
Это расширение, которое я использую для вычисления многострочных высот 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)
}
}
Ответ 12
Если вы используете UILabel с атрибутами, вы можете попробовать метод textRect(forBounds:limitedToNumberOfLines)
.
Это мой пример:
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
label.numberOfLines = 0
label.text = "Learn how to use RxSwift and RxCocoa to write applications that can react to changes in your underlying data without you telling it to do so."
let rectOfLabel = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 0)
let rectOfLabelOneLine = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 1)
let heightOfLabel = rectOfLabel.height
let heightOfLine = rectOfLabelOneLine.height
let numberOfLines = Int(heightOfLabel / heightOfLine)
И мои результаты на детской площадке:
Ответ 13
Вызовите label.sizeToFit
а затем label.layoutIfNeeded()
Тогда вы должны получить правильную высоту с label.frame.height
Ответ 14
Чтобы UILabel соответствовал динамическому контенту, вы можете использовать свойство lines в инспекторе свойств и установить его как 0.
И вам не нужно делать никакого кодирования для этого.
Для более подробной информации вы можете проверить ниже демонстрационное видео
Ответ 15
если вы хотите, чтобы метка принимала динамические строки, вы можете использовать этот
label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text ";
[label sizeToFit];
NSLog(@"Label frame is: %@", NSStringFromCGRect(label.frame));