ios UILabel 有时不能正确包装文本(自动布局)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15513794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UILabel not wrapping text correctly sometimes (auto layout)
提问by Bob Spryn
I have a uilabel setup in a view. It doesn't have a width constraint, but its width is instead determined by a leading constraint to the thumbnail image, and a trailing constraint to the edge of the view.
我在视图中有一个 uilabel 设置。它没有宽度约束,但它的宽度由缩略图图像的前导约束和视图边缘的尾随约束确定。
The label is set to have 0 lines, and to word wrap. My understanding is that this should cause the frame of the uilabel to grow, and indeed it does sometimes. (Previous to auto layout, I would calculate and update the frame of the label in code).
标签设置为 0 行,并自动换行。我的理解是,这应该会导致 uilabel 的框架增长,而且有时确实如此。(在自动布局之前,我会在代码中计算和更新标签的框架)。
So the result is, it works correctly in some instance and not others. See most cells working correctly there, but the last cell appears to be too big. In fact it's the right size. The title "Fair Oaks Smog Check Test" actually ends with "Only". So my calcuation for the cell size is right, it should be that size. However the label doesn't wrap the text for whatever reason. It's frame width does not extend off to the right, so that's not the issue.
所以结果是,它在某些情况下正常工作,而在其他情况下则不正常。看到大多数单元格在那里正常工作,但最后一个单元格似乎太大了。事实上,它的大小是正确的。标题“Fair Oaks 烟雾检查测试”实际上以“仅”结尾。所以我对单元格大小的计算是正确的,应该是那个大小。但是,无论出于何种原因,标签都不会包装文本。它的框架宽度不会向右延伸,所以这不是问题。
So what is going on here? It's 100% consistent, always on that cell and not the ones above it, which makes me think it's related to the size of the text, and UILabel isn't re-laying out the text once this view is added to the cell (which makes it actually smaller width wise).
那么这里发生了什么?它是 100% 一致的,总是在那个单元格上,而不是在它上面的单元格上,这让我认为它与文本的大小有关,并且一旦将此视图添加到单元格(即使其实际上宽度更小)。
Any thoughts?
有什么想法吗?
Some additional information
一些附加信息
The height of the cells is calculated from one sample cell I create and store in a static variable:
单元格的高度是根据我创建并存储在静态变量中的一个示例单元格计算得出的:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.items.count == 0) {
return 60;
}
static TCAnswerBuilderCell *cell = nil;
static dispatch_once_t pred;
dispatch_once(&pred,
^{
// get a sample cellonce
cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL];
});
[cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
return [cell heightForCellWithTableWidth:self.tableView.frame.size.width];
}
I configure the cell with my data object on the fly, and then call a method I have on it which calculates the height of the cell with a given table width (can't always rely on the cell frame being correct initially).
我使用我的数据对象动态配置单元格,然后调用我拥有的方法,该方法计算具有给定表格宽度的单元格的高度(不能总是依赖于最初正确的单元格框架)。
This in turn calls a height method on my view, since it is really where the label lives:
这反过来在我的视图中调用 height 方法,因为它确实是标签所在的位置:
- (CGFloat)heightForCellWithTableWidth:(CGFloat)tableWidth {
// subtract 38 from the constraint above
return [self.thirdPartyAnswerView heightForViewWithViewWidth:tableWidth - 38];
}
This method determines the height by figuring out the correct width of the label, and then doing a calculation:
该方法通过找出标签的正确宽度来确定高度,然后进行计算:
- (CGFloat)heightForViewWithViewWidth:(CGFloat)viewWidth {
CGFloat widthForCalc = viewWidth - self.imageFrameLeadingSpaceConstraint.constant - self.thumbnailFrameWidthConstraint.constant - self.titleLabelLeadingSpaceConstraint.constant;
CGSize size = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(widthForCalc, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
CGFloat returnHeight = self.frame.size.height - self.titleLabel.frame.size.height + size.height;
CGFloat height = returnHeight < self.frame.size.height ? self.frame.size.height : returnHeight;
return height;
}
This works 100% correctly.
这 100% 正确。
The cells are created obviously in cellForRowAtIndexPath and immediately configured:
单元格显然是在 cellForRowAtIndexPath 中创建的,并立即配置:
if (self.items.count > 0) {
TCAnswerBuilderCell *cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL forIndexPath:indexPath];
[cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
return cell;
}
In configuration of the cell, my view is loaded from a nib (it's re-used elsewhere, which is why it's not directly in the cell). The cell adds it as follows:
在单元格的配置中,我的视图是从笔尖加载的(它在其他地方重新使用,这就是它不直接在单元格中的原因)。单元格添加如下:
- (void) configureCellWithThirdPartyObject:(TCThirdPartyObject *)object {
self.detailDisclosureImageView.hidden = NO;
if (!self.thirdPartyAnswerView) {
self.thirdPartyAnswerView = [TCThirdPartyAPIHelper thirdPartyAnswerViewForThirdPartyAPIServiceType:object.thirdPartyAPIType];
self.thirdPartyAnswerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.thirdPartyAnswerView];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_thirdPartyAnswerView]-38-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_thirdPartyAnswerView)]];
}
[self.thirdPartyAnswerView configureViewForThirdPartyObject:object forViewStyle:TCThirdPartyAnswerViewStyleSearchCell];
}
Finally my view configuration looks like this:
最后,我的视图配置如下所示:
- (void) configureViewForThirdPartyObject:(TCTPOPlace *)object forViewStyle:(TCThirdPartyAnswerViewStyle) style {
self.titleLabel.text = object.name;
self.addressLabel.text = [NSString stringWithFormat:@"%@, %@, %@", object.address, object.city, object.state];
self.ratingsLabel.text = [NSString stringWithFormat:@"%d Ratings", object.reviewCount];
NSString *ratingImageName = [NSString stringWithFormat:@"yelp_star_rating_%.1f.png", object.rating];
UIImage *ratingsImage = [UIImage imageNamed:ratingImageName];
if (ratingsImage) {
self.ratingImageView.image = ratingsImage;
}
if (object.imageUrl) {
[self.thumbnailImageView setImageWithURL:[NSURL URLWithString:object.imageUrl] completed:nil];
}
}
A sort of solution, but I don't understand why
一种解决方案,但我不明白为什么
- My subview was designed at 320 width, but has no constraints of its own for width
- The subview was added to the cell, but with horizontal constraints that look like this:
@"|[_thirdPartyAnswerView]-38-|"
- The view was configured immediately after being added to the cell, meaning the text for the titleLabel was set right then.
- For whatever reason, the text was laid out as if the view had the full 320 instead of 282.
- The label was never updated, even though the frame of the subview was updated to 282, and there were constraints on the label that would keep it sized correctly.
- Changing the size of the view in the xib to be 282 fixed the issue, because the label has the right size to begin with.
- 我的子视图设计为 320 宽度,但没有自己的宽度限制
- 子视图已添加到单元格中,但具有如下所示的水平约束:
@"|[_thirdPartyAnswerView]-38-|"
- 视图在添加到单元格后立即配置,这意味着 titleLabel 的文本是在当时设置的。
- 无论出于何种原因,文本的布局就像视图具有完整的 320 而不是 282。
- 标签从未更新,即使子视图的框架已更新为 282,并且标签上存在限制以保持其大小正确。
- 将 xib 中视图的大小更改为 282 解决了该问题,因为标签具有正确的大小开始。
I'm still not understanding why the label doesn't re-lay out after the size of the parent view is updated when it has both leading and trailing constraints.
我仍然不明白为什么标签在具有前导和尾随约束时更新父视图的大小后不重新布局。
SOLVED
解决了
See Matt's answer below: https://stackoverflow.com/a/15514707/287403
请参阅下面的马特回答:https: //stackoverflow.com/a/15514707/287403
In case you don't read the comment, the primary problem was that I was unknowingly setting preferredMaxLayoutWidth
via IB when designing a view at a bigger width than it would be shown (in some cases). preferredMaxLayoutWidth
is what is used to determine where the text wraps. So even though my view and titleLabel correctly resized, the preferredMaxLayoutWidth
was still at the old value, and causing wrapping at unexpected points. Setting the titleLabel instead to it's automatic size (?= in IB), and updating the preferredMaxLayoutWidth
dynamically in layoutSubviews
before calling super was the key. Thanks Matt!
如果您没有阅读评论,主要问题是我preferredMaxLayoutWidth
在设计宽度大于显示宽度的视图时不知不觉地通过 IB设置(在某些情况下)。preferredMaxLayoutWidth
用于确定文本换行的位置。因此,即使我的视图和 titleLabel 正确调整了大小,它preferredMaxLayoutWidth
仍然处于旧值,并导致在意外点处换行。将 titleLabel 设置为它的自动大小(?= 在 IB 中),并在调用 super 之前preferredMaxLayoutWidth
动态更新layoutSubviews
是关键。谢谢马特!
回答by matt
I'm someone who has written an app that uses autolayout of five labels in a cell in a table whose cells have different heights, where the labels resize themselves according to what's in them, and it does work. I'm going to suggest, therefore, that the reason you're having trouble might be that your constraints are under-determining the layout - that is, that you've got ambiguous layout for the elements of the cell. I can't test that hypothesis because I can't see your constraints. But you can easily check (I think) by using po [[UIWindow keyWindow] _autolayoutTrace]
when paused in the debugger.
我编写了一个应用程序,该应用程序在表格中的一个单元格中使用五个标签的自动布局,其单元格具有不同的高度,其中标签根据其中的内容调整自己的大小,并且确实有效。因此,我将建议您遇到问题的原因可能是您的约束未确定布局 - 也就是说,您对单元格元素的布局不明确。我无法测试该假设,因为我看不到您的限制。但是您可以通过po [[UIWindow keyWindow] _autolayoutTrace]
在调试器中暂停时使用来轻松检查(我认为)。
Also I have one other suggestion (sorry to just throw stuff at you): make sure you're setting the label's preferredMaxLayoutWidth
. This is crucial because it's the width at which the label will stop growing horizontally and start growing vertically.
另外我还有一个建议(抱歉只是向你扔东西):确保你设置了标签的preferredMaxLayoutWidth
. 这是至关重要的,因为它是标签将停止水平增长并开始垂直增长的宽度。
回答by phatmann
I had the same problem and solved it using a suggestion from this answer. In a subclass of UILabel I placed this code:
我遇到了同样的问题并使用此答案中的建议解决了它。在 UILabel 的子类中,我放置了以下代码:
- (void)layoutSubviews
{
[super layoutSubviews];
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
I don't understand why this is not the default behavior of UILabel, or at least why you cannot just enable this behavior via a flag.
我不明白为什么这不是 UILabel 的默认行为,或者至少为什么你不能通过标志启用这种行为。
I am a little concerned that preferredMaxLayoutWidth is being set in the middle of the layout process, but I see no easy way around that.
我有点担心在布局过程中设置了 preferredMaxLayoutWidth,但我认为没有简单的方法可以解决这个问题。
回答by Sergiu Todirascu
Also, check that you are passing integral numbers to your layout constraints in code.
此外,检查您是否将整数传递给代码中的布局约束。
For me it happened that after some calculations (e.g. convertPoint:toView:), I was passing in something like 23.99999997, and eventually this lead to a 2-line label displaying as a one-liner (although its frame seemed to be calculated correctly). In my case CGRectIntegral did the trick!
对我来说,经过一些计算(例如 convertPoint:toView:),我传入了类似 23.99999997 的东西,最终这导致 2 行标签显示为一行(尽管它的框架似乎计算正确) . 就我而言,CGRectIntegral 成功了!
Rounding errors could kill ya :)
舍入错误可能会杀死你:)