如何dynamic计算UILabel高度?
我想根据给定的文本dynamic地计算UILabel的行数和高度。
尝试这个
// 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
通过使用类别,只需创build名为的类别类
的UILabel + UILabelDynamicHeight.h
的UILabel + UILabelDynamicHeight.m
高度计算没有更多的紧张感。 请查看下面的实现。
iOS7及以上版本,iOS 7以下的更新:dynamic计算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
在UILabel实例上调用-sizeToFit
会自动调整它的大小以适应它显示的文本,不需要计算。 如果你需要这个尺寸,你可以从标签的框架属性中获取。
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));
总而言之,您可以使用string并调用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: [NSFontAttributeName: label.font], context: nil) let labelHeight = actualLabelSize.height
一些扩展来做到这一点:
Swift版本:
extension UILabel { func textHeight(for width: CGFloat) -> CGFloat { guard let text = text else { return 0 } return text.height(for: width, font: font) } func attributedTextHeight(for width: CGFloat) -> CGFloat { guard let attributedText = attributedText else { return 0 } return attributedText.height(for: width) } } extension String { func height(for width: CGFloat, font: UIFont) -> CGFloat { let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let actualSize = self.boundingRect(for: maxSize, options: [.usesLineFragmentOrigin], attributes: [NSFontAttributeName: font], context: nil) return actualSize.height } } extension NSAttributedString { func height(for width: CGFloat) -> CGFloat { let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let actualSize = boundingRect(for: 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
目前的解决scheme从iOS 7开始已被弃用。
这是一个更新的解决scheme:
+ (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; }
我的解决scheme是这样设置的原因是因为我正在使用一个UITableViewCell并相对于标签占用多less空间dynamic调整单元格的大小。
在不调用sizeToFit的情况下,您可以通过一个非常简单的即插即用解决scheme来完成数字化操作:
+ (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; }
我使用它有很多dynamic分配高度的UITableViewCells。
解决属性问题以及@Salman Zaidi。
CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX); CGSize requiredSize = [lbl sizeThatFits:maxSize]; CGFloat height=requiredSize.height
复制并粘贴此方法并使用它就像:
[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; }
在我的情况下,我为每个部分使用一个固定大小的标题 ,但在每个标题中有一个dynamic单元大小 。 单元格的高度取决于标签的高度。
使用:
tableView.estimatedRowHeight = SomeNumber tableView.rowHeight = UITableViewAutomaticDimension
工作, 但使用时:
tableView.reloadSections(IndexSet(integer: sender.tag) , with: .automatic)
当大量的头文件没有被折叠的时候,甚至在使用types为.none
(FYI,一个固定的头文件)时,创build了许多错误,例如头重复(相同types下的头文件typesx)和奇怪的animation身高和细胞高度的作品)。
解决scheme是使用heightForRowAt
callback,并计算标签的高度由你自己(加上animation看起来好多了 )。 请记住,高度被称为第一 。
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) } }
您需要创build一个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) }
您必须发送标签的宽度
如果你想标签采取dynamic行,你可以使用这个
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));