UILabel sizeToFit 不适用于自动布局 ios6
Posted
技术标签:
【中文标题】UILabel sizeToFit 不适用于自动布局 ios6【英文标题】:UILabel sizeToFit doesn't work with autolayout ios6 【发布时间】:2013-04-07 05:26:13 【问题描述】:我应该如何以编程方式(以及以哪种方法)配置高度取决于其文本的 UILabel?我一直在尝试使用 Storyboard 和代码的组合来设置它,但无济于事。大家推荐sizeToFit
,同时设置lineBreakMode
和numberOfLines
。但是,无论我将该代码放在viewDidLoad:
、viewDidAppear:
还是viewDidLayoutSubviews
中,我都无法让它工作。要么我让长文本框太小,它不会变大,要么我让它太大,它不会缩小。
【问题讨论】:
FWIW 相同的代码:我不需要在 Xcode/viewController 中使用label.sizeToFit()
,约束就足够了。没有在 Playground 中创建标签。到目前为止,我发现它在 Playground 中工作的唯一方法是 label.sizeToFit()
【参考方案1】:
请注意,在大多数情况下,Matt's solution 会按预期工作。但如果它不适合您,请进一步阅读。
要让您的标签自动调整高度,您需要执行以下操作:
-
为标签设置布局约束
以低优先级设置高度约束。它应该低于 ContentCompressionResistancePriority
设置 numberOfLines = 0
将 ContentHuggingPriority 设置为高于标签的高度优先级
为标签设置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_)]];
使用界面生成器
设置四个约束。高度限制是强制性的。
然后转到标签的属性检查器并将行数设置为 0。
转到标签的尺寸检查器并增加垂直 ContentHuggingPriority 和垂直 ContentCompressionResistancePriority。
选择和编辑高度限制。
并降低高度限制优先级。
享受吧。 :)
【讨论】:
是的!这正是我一直在寻找的答案。我唯一需要做的就是将 contentHuggingPriority 和 contentCompressionResistancePriority 设置为 required。我可以在IB中做到这一切!非常感谢。 你想多了。设置preferredMaxLayoutWidth
或固定标签的宽度(或其左右两侧)。就这样!然后它将自动垂直增长和收缩以适应其内容。
我必须设置 preferredMaxLayoutWidth 属性 + 固定标签的宽度。像魅力一样工作:)
preferredMaxLayoutWidth 和/或固定左右两侧不是绝对的。内容拥抱和压缩优先级将始终有效,只要它们的优先级高于其他约束。
^ 我终于找到了原因,可能值得一提的是,当您将单元格粘在视图底部时,您需要将“to bottom”约束的优先级设置为 less然后 1000 我把它放在 750 上,一切正常。我猜它缩小了视野。【参考方案2】:
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));
【讨论】:
【参考方案3】:在 ios 6 中,使用自动布局,如果 UILabel 的边(或宽度)和顶部被固定,它会自动垂直增长和收缩以适应其内容,根本不需要代码 并且没有弄乱它的抗压性或其他任何东西。这很简单。
在更复杂的情况下,只需设置标签的preferredMaxLayoutWidth
。
无论哪种方式,正确的事情都会自动发生。
【讨论】:
您还需要确保将 numberOfLines 设置为 0,否则您将无法换行。 这对我来说还不够。还需要@Mark 的回答中的第 2 点。 不写代码也可以让标签自动增长几行? 这比公认的答案要简单得多。我做了两个例子(here 和 here)来说明这个答案。 @Suragch 使用约束这是可行的。但它不适用于 Playground 中的相同代码。在操场上你必须有label.sizeToFit()
【参考方案4】:
在 IOS7 中注意到 sizeToFit 也无法正常工作 - 也许该解决方案也可以帮助您
[textView sizeToFit];
[textView layoutIfNeeded];
【讨论】:
@Rambatino 这对我有用。需要setNeedsUpdateConstraints
时添加layoutIfNeeded
。但情况可能会有所不同。
当我做一个弹出窗口时,这对我来说效果最好。不过,我需要先做 layoutIfNeeded【参考方案5】:
虽然问题以编程方式说明,遇到了同样的问题,并且更喜欢在 Interface Builder 中工作,但我认为使用 Interface Builder 解决方案添加到现有答案可能会很有用。
首先要忘记sizeToFit
。自动布局将根据内在内容大小代表您处理此问题。
因此,问题是,如何使用自动布局获取标签以适应其内容?具体来说 - 因为问题提到了它 - 高度。请注意,同样的原则也适用于宽度。
让我们从一个高度设置为 41px 高的 UILabel 示例开始:
正如您在上面的屏幕截图中所见,"This is my text"
在上方和下方都有填充。这是 UILabel 的高度和它的内容文本之间的填充。
如果我们在模拟器中运行应用程序,果然,我们看到的都是一样的:
现在,让我们在 Interface Builder 中选择 UILabel,并查看 Size 检查器中的默认设置:
请注意上面突出显示的约束。这就是Content Hugging Priority。正如 Erica Sadun 在出色的 iOS Auto Layout Demystified 中所描述的那样:
视图倾向于避免围绕其核心内容进行额外填充的方式
对于我们来说,有了 UILabel,核心内容就是文字。
这里我们来到了这个基本场景的核心。我们给了我们的文本标签两个约束。他们冲突。有人说“高度必须等于 41 像素高”。另一个说“拥抱它的内容,这样我们就没有任何额外的填充”。在我们的例子中,将视图拥抱它的 text,这样我们就没有任何额外的填充。
现在,使用自动布局,两个不同的指令说做不同的事情,运行时必须选择一个或另一个。它不能两者兼得。 UILabel 不能同时为 41 像素高, 和 没有填充。
解决方法是指定优先级。一条指令必须具有比另一条更高的优先级。如果两条指令说的不同,并且具有相同的优先级,则会发生异常。
所以让我们试一试吧。我的身高限制的优先级为 1000,这是必需。内容拥抱高度250,弱。如果我们将高度限制优先级降低到 249,会发生什么?
现在我们可以看到神奇的开始发生了。让我们在 sim 中尝试:
太棒了!实现内容拥抱。只是因为高度优先级 249 小于内容拥抱优先级 250。基本上,我是说“我在这里指定的高度不如我为内容拥抱指定的高度重要”。所以,拥抱的内容获胜。
底线,让标签适合文本可以像指定高度或宽度约束一样简单,并正确设置与该轴的内容拥抱优先级约束相关联的优先级。
将等价的宽度作为练习留给读者!
【讨论】:
非常感谢您的精彩解释!终于明白内容拥抱优先级了。 能否请您解释一下什么是内容压缩阻力优先级?谢谢! 优秀的回答 Max!有没有办法限制以这种方式工作的行数?【参考方案6】:在我的例子中,我创建了一个包含 UILabel(长度未知)的 UIView 子类。在 iOS7 中,代码很简单:设置约束,不用担心内容拥抱或压缩阻力,一切都按预期工作。
但在 iOS6 中,UILabel 总是被剪裁成一行。上面的答案都不适合我。内容拥抱和压缩阻力设置被忽略。防止剪辑的唯一解决方案是在标签上包含一个 preferredMaxLayoutWidth。但是我不知道将首选宽度设置为什么,因为它的父视图的大小是未知的(实际上,它将由内容定义)。
我终于找到了解决方案here。因为我正在处理自定义视图,所以我可以在计算一次约束后添加以下方法来设置首选布局宽度,然后重新计算它们:
- (void)layoutSubviews
// Autolayout hack required for iOS6
[super layoutSubviews];
self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
[super layoutSubviews];
【讨论】:
这正是我遇到的情况……有时很高兴知道你并不孤单。 亚当,我正在为你所说的在 ios7 中完美运行而苦苦挣扎。我有一个带有 uiview 和 uilabel 的自定义单元格。我可以让标签适合内容,但视图不能,无论我对其施加什么限制。你是怎么让它工作的?我发誓我尝试了所有方法,但没有达到标签的高度【参考方案7】:确保标签的 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
【讨论】:
【参考方案8】:我已经解决了 xCode6 将“首选宽度”设置为自动并将标签固定在顶部、前导和尾随
【讨论】:
【参考方案9】:在我的情况下,当在 UITableViewCell 中使用标签时,标签的大小会调整,但高度会超出表格单元格的高度。这对我有用。我按照 Max MacLeod 做了,然后确保单元格高度设置为 UITableViewAutomaticDimension。
您可以在您的 init 或 awakeFromNib 中添加它,
self.tableView.rowHeight = UITableViewAutomaticDimension.
在情节提要中,选择单元格,打开大小检查器,并通过选中“自定义”复选框确保行高设置为“默认”。
8.0 中有一个问题也需要在代码中进行设置。
【讨论】:
【参考方案10】:我在 UIView 子类中也遇到了这个问题,该子类包含一个 UILabel 作为其内部元素之一。即使使用自动布局并尝试了所有推荐的解决方案,标签也不会将其高度收紧到文本。就我而言,我只希望标签是一行。
无论如何,对我有用的是为 UILabel 添加所需的高度约束,并在调用 intrinsicContentSize 时手动将其设置为正确的高度。如果您没有 UILabel 包含在另一个 UIView 中,您可以尝试子类化 UILabel 并通过首先设置高度约束然后返回 [super instrinsicContentSize]; 而不是 [ self.containerview intrinsiceContentSize];就像我在下面做的那样,这是特定于我的 UIView 子类的。
- (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 上完美运行。
【讨论】:
值得注意的是,如果您正确设置了视图(即您的约束是正确的,并且您在视图生命周期的正确阶段设置了所有内容),则永远不必覆盖 @ 987654322@。运行时应该能够根据您正确设置的约束来计算此值。 理论上这应该是正确的,但我们无法访问 Apple 的所有私有 API 和实现,它们并非万无一失,而且代码中确实存在错误。 ...但是 [super intrinsicContentSize] 应该照顾它,这就是我的意思。如果您正确实现了自动布局。【参考方案11】:我觉得我应该做出贡献,因为我花了一段时间才找到正确的解决方案:
我们的目标是让自动布局在不调用 sizeToFit() 的情况下完成其工作,我们将通过指定正确的约束来做到这一点: 在 UILabel 上指定顶部、底部和前导/尾随空格限制 将行数属性设置为 0 将内容拥抱优先级增加到 1000 将内容压缩阻力优先级降低到 500 在底部容器约束中,将优先级降低到 500基本上,你会告诉你的 UILabel,即使它有一个固定的高度约束,它也可以打破约束,使自己更小,以便拥抱内容(例如,如果你有单行),但它不能打破约束使其变大。
【讨论】:
【参考方案12】:我以编程方式添加了UILabel
,就我而言,这就足够了:
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0
【讨论】:
【参考方案13】:对我有用的解决方案;如果您的 UILabel 具有固定宽度,请在接口文件中将约束从 constant =
更改为 constant <=
【讨论】:
以上是关于UILabel sizeToFit 不适用于自动布局 ios6的主要内容,如果未能解决你的问题,请参考以下文章
如何在自动布局中使用 NSLineBreakByTruncatingMiddle 和 sizeToFit 实现 Multiline UILabel?