如何以编程方式在 UILabel 上设置 sizeToFit 宽度和高度?
Posted
技术标签:
【中文标题】如何以编程方式在 UILabel 上设置 sizeToFit 宽度和高度?【英文标题】:How to programmatically sizeToFit width AND height on UILabel? 【发布时间】:2018-08-02 16:51:52 【问题描述】:我正在以编程方式创建多行 UILabel ([label setNumberOfLines:0];
)。
UILabel 的内置sizeToFit
方法对1 行UILabels 效果很好,但是对于多行文本,它设置了适当的高度,但宽度设置得太小,导致较长的文本行换行。
直到用户输入他们的文本后,我才知道标签宽度。我想调整标签的大小以适应最长文本行的宽度。根据@DonMag 的评论,我还想限制标签不比屏幕宽。
我尝试了不同的lineBreakMode
设置,但没有“nowrap”选项。
我已经搜索过,有很多相关的解决方案,但没有一个可以解决宽度和高度的 sizeToFit 问题。
有没有办法以编程方式调整多行 UILabel 的大小以适应文本的宽度和高度?
【问题讨论】:
你不应该需要sizeToFit
和多行 UILabels... 这个想法是你设置标签的宽度,让换行设置处理换行符,让自动-布局处理高度变化。
使用自动布局来固定前后距离(宽度)。然后sizeToFit
会处理高度。
@DonMag - 我理解你的意思,但我的标签确实需要 sizeToFit。在用户输入文本之前,我不知道标签的宽度。我希望 UILabel 调整为多行文本中最长行的宽度。我会更新我的问题。
@Koen - 您能解释一下如何以编程方式执行此操作吗?我的标签不是在情节提要中创建的。
@ByteSlinger - 这没有意义......“最长的线”将是无限的宽度,延伸到视图的边缘之外。您是否想说“我希望标签的宽度最多 300 磅,并且在自动换行后我希望标签背景看起来就像文本完美契合一样”?
【参考方案1】:
您可以使用boundingRectWithSize
...
将你的标签添加到视图中,并给它一个起始宽度约束(不管是什么值,因为它会被改变)。
保留对该宽度约束的引用(如果您使用 IB,IBOutlet 可以正常工作)。
不要给它一个高度限制。
当你设置标签的文本时,你可以使用它来改变它的宽度:
// get the font of the label
UIFont *theFont = _theLabel.font;
// get the text of the label
NSString *theString = _theLabel.text;
// calculate the bounding rect, limiting the width to the width of the view
CGRect r = [theString boundingRectWithSize:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:@NSFontAttributeName: theFont
context:nil];
// change the constant of the constraint to the calculated width
_theWidthConstraint.constant = ceil(r.size.width);
// NOTE: If you are *not* using auto-layout,
// this same calculation can be used to explicitly set
// the .frame of the label.
编辑:
根据 OP 的要求,一个完整的、可运行的示例——仅使用代码,没有故事板/IB——可以在这里找到:https://github.com/DonMag/MultilineLabelFitWidth
编辑 2:
GitHub 项目已更新...现在包含手动框架设置和自动布局/约束的示例。
【讨论】:
你不需要 IB 来使用自动布局。我用 GitHub 上一个完整项目的链接编辑了我的答案,该项目使用 code only - 没有故事板或 IB(启动屏幕除外)。 用手动框架设置示例更新了 GitHub 项目。 感谢@DonMag。请注意,您的 GitHub 示例仍使用 IB 来创建初始 UILabel,而我的请求是以编程方式创建(第一行)并调整它们的大小(最后一行)(正如我在回答中所做的那样)。我仍然会选择您的答案,因为您提供了 boundingRectWithSize 技巧。而且因为您为此付出了很多时间和精力。 嗯??? GitHub 项目将 IB only 用于启动屏幕。没有“Main.storyboard”,也没有 XIB 文件...一切都在代码中完成。【参考方案2】:通过更多的实验,我发现了一些我在 SO(还...)中没有看到的技巧。一般来说,它是这样工作的:
查找最长的文本行 将 numberOfLines 设置为 1(暂时) 将标签文本设置为最长的文本行 调用 label.sizeToFit(设置最长线的标签宽度) 将 numberOfLines 设置为 0(多行) 将标签文本设置为完整的多行文本 调用 label.sizeToFit(设置所有行的标签高度)瞧!现在,您的 UILabel 已调整大小以适合您的多行文本。
这是一个示例(GitHub 上的演示项目:UILabelSizeToFitDemo):
- (UILabel *)label = nil;
- (void)updateLabel:(NSString *)notes
// close to the "sticky" notes color
UIColor *bananaColor = [ViewController colorWithHexString:@"#FFFC79"];
if (_label == nil)
_label = [[UILabel alloc] init];
_label.numberOfLines = 0;
_label.textColor = UIColor.blackColor;
[_label setBackgroundColor:[bananaColor colorWithAlphaComponent:0.9f]];
_label.textAlignment = NSTextAlignmentLeft;
[self.view addSubview:_label];
// make font size based on screen size
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fontSize = MIN(screenWidth,screenHeight) / 12;
[_label setFont:[UIFont systemFontOfSize:fontSize]];
// split lines
NSArray *lines = [notes componentsSeparatedByString:@"\n"];
NSString *longestLine = lines[0]; // prime it with 1st line
// fill a temp UILabel with each line to find the longest line
for (int i = 0; i < lines.count; i++)
NSString *line = (NSString *)lines[i];
if (longestLine == nil || line.length > longestLine.length)
longestLine = line;
// force UILabel to fit the largest line
[_label setNumberOfLines:1];
[_label setText:longestLine];
[_label sizeToFit];
// make sure it doesn't go off the screen
if (_label.frame.size.width > screenWidth)
CGRect frame = _label.frame;
frame.size.width = screenWidth - 20;
_label.frame = frame;
// now fill with the actual notes (this saves the previous width)
[_label setNumberOfLines:0];
[_label setText:notes];
[_label sizeToFit];
// center the label in my view
CGPoint center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
[_label setCenter:center];
更新:这是一个替代的完整解决方案,使用来自@DonMag 的代码 sn-p 中的boundinRectWithSize
:
-(void)updateLabel:(NSString *)notes
// close to the "sticky" notes color
UIColor *bananaColor = [ViewController colorWithHexString:@"#FFFC79"];
if (_label == nil)
_label = [[UILabel alloc] init];
_label.numberOfLines = 0;
_label.textColor = UIColor.blackColor;
_label.backgroundColor = [bananaColor colorWithAlphaComponent:0.9f];
_label.textAlignment = NSTextAlignmentLeft;
[self.view addSubview:_label];
// set new text
_label.text = notes;
// make font size based on screen size
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fontSize = MIN(screenWidth,screenHeight) / 12;
[_label setFont:[UIFont systemFontOfSize:fontSize]];
// calculate the bounding rect, limiting the width to the width of the view
CGRect frame = [notes boundingRectWithSize:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:@NSFontAttributeName: _label.font
context:nil];
// set frame and then use sizeToFit
[_label setFrame:frame];
[_label sizeToFit];
// center the label in my view
CGPoint center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
[_label setCenter:center];
【讨论】:
以上是关于如何以编程方式在 UILabel 上设置 sizeToFit 宽度和高度?的主要内容,如果未能解决你的问题,请参考以下文章
以编程方式在 UICollectionView 内对齐 UILabel 大小