UILabel sizeToFit不适用于自动布局iOS6


152

我应该如何以编程方式(和使用哪种方法)配置高度取决于其文本的UILabel?我一直在尝试使用Storyboard和代码的组合进行设置,但无济于事。每个人sizeToFit在设置lineBreakMode和时都建议numberOfLines。但是,不管我把代码viewDidLoad:viewDidAppear:或者viewDidLayoutSubviews我不能得到它的工作。我要么将框设置得太小而不能容纳较长的文本,否则它不会增大,或者我将其设置得太大而不会收缩。


FWIW相同的代码:我不需要label.sizeToFit()在Xcode / viewController中使用,约束就足够了。没有在Playground中创建标签。到目前为止,我发现它只能在Playground中使用的唯一方法是label.sizeToFit()
Honey

Answers:


407

请注意,在大多数情况下,Matt的解决方案可以按预期工作。但是,如果对您不起作用,请进一步阅读。

要使标签自动调整高度,您需要执行以下操作:

  1. 设置标签的布局约束
  2. 设置高度限制为低优先级。它应该低于ContentCompressionResistancePriority
  3. 设置numberOfLines = 0
  4. 将ContentHuggingPriority设置为高于标签的高度优先级
  5. 设置label的preferredMaxLayoutWidth。标签使用该值来计算其高度

例如:

self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;

[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];

NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[descriptionLabel_(220@300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];

使用界面生成器

  1. 设置四个约束。高度限制是强制性的。 在此处输入图片说明

  2. 然后转到标签的属性检查器,并将行数设置为0。 在此处输入图片说明

  3. 转到标签的大小检查器,并增加垂直ContentHuggingPriority和垂直ContentCompressionResistancePriority。
    在此处输入图片说明

  4. 选择并编辑高度约束。
    在此处输入图片说明

  5. 并降低高度约束优先级。
    在此处输入图片说明

请享用。:)


4
是!这正是我一直在寻找的答案。我唯一需要做的就是将contentHuggingPriority和contentCompressionResistancePriority设置为required。我可以在IB中做所有事情!非常感谢。
circuitlego 2013年

43
您想得太多了。设置preferredMaxLayoutWidth或固定标签的宽度(或其右侧和左侧)。就这样!然后,它将自动垂直增大和缩小以适合其内容。
马特

我必须设置preferredMaxLayoutWidth属性+固定标签的宽度。像魅力一样工作:)
MartinMoizard 2013年

preferredMaxLayoutWidth和/或固定左侧和右侧不是绝对的。只要内容优先级和压缩优先级高于其余约束条件,它们就始终有效。
埃里克·

2
^并且我终于发现为什么,可能值得一提的是,当单元格停留在视图底部时,您需要将“底部”约束的优先级设置为小于1000,我将其放在750上,一切工作正常。我想它缩小了视野。
Mathijs Segers 2015年

65

在iOS 6中,使用自动布局,如果将UILabel的侧面(或宽度)和顶部固定,则它会自动垂直增大和缩小以适合其内容,而无需编写任何代码,也不会破坏其抗压强度或其他任何特性。这很简单。

在更复杂的情况下,只需设置标签的preferredMaxLayoutWidth

无论哪种方式,正确的事情都会自动发生。


12
您还需要确保将numberOfLines设置为0,否则将不会自动换行。
devongovett

2
这对我来说还不够。还需要@Mark答案中的第2点。
2013年

是否可以在不编写代码的情况下使标签在多行中自动增长?
2014年

这比公认的答案要简单得多。我做了两个例子(这里这里)来说明这个答案。
Suragch '16

@Suragch使用约束可以正常工作。但是它不适用于Playground中的相同代码。在操场上,您必须拥有label.sizeToFit()
Honey

42

尽管问题是通过编程方式提出的,也遇到过相同的问题,并且希望在Interface Builder中工作,但我认为使用Interface Builder解决方案添加到现有答案中可能会很有用。

第一件事就是忘记sizeToFit。自动版式将根据内部内容的大小代表您处理此问题。

因此,问题是,如何使用自动版式获取标签以使其内容适合?具体来说-因为问题提到了-身高。注意,相同的原理也适用于宽度。

因此,让我们从一个UILabel示例开始,该示例的高度设置为41px高:

在此处输入图片说明

如您在上方屏幕抓取中所见,在"This is my text"上方和下方都有填充。那就是在UILabel的高度及其内容(即文本)之间填充。

如果我们确实在模拟器中运行该应用程序,那么肯定会看到相同的内容:

在此处输入图片说明

现在,让我们在Interface Builder中选择UILabel,然后查看Size检查器中的默认设置:

在此处输入图片说明

请注意上面突出显示的约束。那就是内容拥抱优先。正如Erica Sadun在出色的iOS Auto Layout Demystified中所描述的那样,它是:

视图倾向于避免在其核心内容周围进行额外填充的方式

对于我们来说,使用UILabel,核心内容是文本。

在这里,我们介绍了此基本方案的核心。我们给了文本标签两个约束。他们发生冲突。有人说“高度必须等于41像素高”。另一个说“拥抱它的内容,这样我们就没有多余的填充了”。在我们的例子中,将视图拥抱为其文本,因此我们没有任何多余的填充。

现在,使用自动版式时,两条不同的指令说要做不同的事情,运行时必须选择一个或另一个。它不能同时做到。所述的UILabel不能同时为41个像素高,并且具有没有填充。

解决此问题的方法是指定优先级。一个指令必须具有比另一指令更高的优先级。如果两个指令说的东西不同,并且具有相同的优先级,则将发生异常。

因此,让我们开始吧。我的身高约束的优先级为1000,这是必需的。内容拥抱高度为250较弱。如果将高度限制优先级降低到249,会发生什么?

在此处输入图片说明

现在我们可以看到魔术开始发生了。让我们尝试在SIM卡中:

在此处输入图片说明

太棒了!实现了内容拥抱。仅因为高度优先级249小于包含优先级250的内容。基本上,我是说“我在此处指定的高度比为内容拥抱指定的高度重要”。因此,内容拥抱必胜。

底线,使标签适合文本可以很简单,只需指定高度-或宽度-约束,然后将优先级与该轴包含优先级约束的内容相关联地正确设置即可。

留给读者做相当于宽度的练习!


3
非常感谢您的出色解释!最终,我了解了内容拥抱优先级。
kernix 2014年

您能否解释一下“内容压缩优先级”是什么?谢谢!
kernix 2014年

2
Excelente回答Max!是否有某种方式可以限制以这种方式工作的行数?
rafaeljuzo

7

在IOS7中注意到sizeToFit也无法正常工作-也许该解决方案也可能对您有所帮助

[textView sizeToFit];
[textView layoutIfNeeded];

1
@Rambatino这对我有用。layoutIfNeeded在需要时添加setNeedsUpdateConstraints。但是情况可能有所不同。
2014年

当我做一个弹出窗口时,这对我来说效果最好。不过,我需要先进行layoutIfNeeded
Jason

4

确保标签的preferredMaxLayoutWidth与标签的宽度保持同步的另一种选择:

#import "MyLabel.h"

@implementation MyLabel

-(void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    // This appears to be needed for iOS 6 which doesn't seem to keep
    // label preferredMaxLayoutWidth in sync with its width, which 
    // means the label won't grow vertically to encompass its text if 
    // the label's width constraint changes.
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

@end

4

我觉得我应该做出自己的贡献,因为我花了一些时间才找到正确的解决方案:

  • 目的是让Auto Layout无需调用sizeToFit()就可以完成工作,我们将通过指定正确的约束条件来做到这一点:
  • 在UILabel上指定顶部,底部和前导/尾随空间约束
  • 将行数属性设置为0
  • 将内容拥抱优先级增加到1000
  • 将内容抗压缩优先级降低到500
  • 在底部容器约束下,将优先级降低到500

基本上,发生的事情是告诉您UILabel,即使它具有固定的高度约束,也可以打破约束以使其自身变小以拥抱内容(例如,如果您只有一行),但是它不能打破约束,使其更大。


3

就我而言,我正在创建一个包含UILabel(长度未知)的UIView子类。在iOS7中,代码简单明了:设置约束,不必担心内容拥挤或抗压缩性,一切都按预期进行。

但是在iOS6中,UILabel始终被剪切为一行。上面的答案都不适合我。内容拥抱和抗压缩性设置被忽略。防止裁剪的唯一解决方案是在标签上包含preferredMaxLayoutWidth。但是我不知道将首选宽度设置为什么,因为其父视图的大小是未知的(实际上,它将由内容定义)。

我终于在这里找到了解决方案。因为我正在使用自定义视图,所以可以只添加以下方法来设置约束一次,然后计算约束,然后设置首选的布局宽度:

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}

这是我遇到的确切情况……有时很高兴知道您并不孤单。
daveMac 2014年

亚当,我在为您所说的在ios7中完美运行而苦苦挣扎。我有一个带有uiview和uilabel的自定义单元格。无论我施加什么约束,我都可以使标签适合内容,但视图不适合。您是如何工作的?我发誓我尝试了一切,但并没有达到标签的高度
denikov

3

我以UILabel编程方式添加,在我的情况下就足够了:

label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0

2

我已经使用xCode6解决了问题,将“首选宽度”设置为“自动”,并将标签固定在顶部,顶部和底部 在此处输入图片说明


0
UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
                             constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
                                 lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));

0

我也遇到了一个UIView子类(如果包含UILabel作为其内部元素)也遇到了这个问题。即使使用自动布局并尝试所有推荐的解决方案,标签也不会将其高度紧贴文本。就我而言,我只希望标签为一行。

无论如何,对我有用的是为UILabel添加所需的高度约束,并在调用internalContentSize时将其手动设置为正确的高度。如果另一个UIView中没有包含UILabel,则可以尝试对UILabel进行子类化,并通过首先设置高度限制然后返回
[super instrinsicContentSize]来提供类似的实现而不是[self.containerview internaleContentSize]; 就像我在下面做的,这是针对我的UIView sublass的。

- (CGSize)intrinsicContentSize
{
    CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
    self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
    return [self.containerView intrinsicContentSize];

}

现在可以在iOS 7和iOS 8上完美运行。


值得注意的是,如果您正确设置了视图(即您的约束正确,并且已在视图生命周期的正确阶段设置了所有内容),则永远不必覆盖intrinsicContentSize。运行时应该能够基于正确的设置约束条件来计算此值。
John Rogers

从理论上讲应该是正确的,但是我们不能访问所有Apple的私有API和实现,并且它们不是万无一失的,并且它们的代码中确实存在错误。
n8tr

...但是我要说的是[super internalContentSize]应该照顾好它。如果您正确实现了自动布局。
约翰·罗杰斯

0

对我有用的解决方案;如果您的UILabel有固定的宽度,约束从改变constant =constant <=你的接口文件

在此处输入图片说明


-1

在我的情况下,当在UITableViewCell中使用标签时,标签处的大小会重新调整,但高度会超出表格单元格的高度。这对我有用。我根据Max MacLeod进行了操作,然后确保将单元格高度设置为UITableViewAutomaticDimension。

您可以在init或awakeFromNib中添加它,

self.tableView.rowHeight = UITableViewAutomaticDimension.  

在情节提要中,选择单元格,打开“大小”检查器,并通过选中“自定义”复选框确保行高设置为“默认”。

8.0中存在一个问题,该问题也要求在代码中进行设置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.