在我正在创建的这个 iOS 8 应用程序中,我有一个表格视图,我需要它们自行调整大小.我使用自动布局实现了它并且它工作.几乎.这是它现在的样子.
一个单元格内有 3 个标签.具有 lorem ipsum 文本的主标签.具有数字字符串的字幕(这是两个单独的标签.可能会因为它们具有相同的颜色而令人困惑.)然后是带有黑色小文本的第三个标签.
第一个标签正确地调整了自己的大小,没有问题,第二个标签相应地上下移动.但问题在于第三个小标签.如您所见,它不会自行调整大小以适应所有文本.
现在发生了一件奇怪的事情.我把它变成横向的,就是这样.
由于有空间,标签显示了它应该显示的整个文本.美好的.然后我把它转回纵向.
现在小标签已调整大小以适应其所有文本,但它溢出了单元格边界.我试着让细胞变大,但没有用.由于这是自调整大小的单元格,我认为这甚至不是正确的方法.
我的自动布局约束也没有收到任何错误甚至警告.
我已经在viewDidLoad()
方法中设置了这两行代码.
tableView.estimatedRowHeight = 100tableView.rowHeight = UITableViewAutomaticDimension
谁能告诉我我在这里做错了什么?
由于仅通过查看图像很难回答,并且我没有更多代码可以在上述代码段旁边发布,因此我上传了一个可运行的 Xcode 项目来演示该问题 .
这里的问题在于多行标签的 preferredMaxLayoutWidth
属性.这是告诉标签何时应该自动换行的属性.必须正确设置才能使每个标签的 intrinsicContentSize
具有正确的高度,这最终是自动布局用来确定单元格高度的.
Xcode 6 Interface Builder 引入了一个新选项,可以将此属性设置为 Automatic.不幸的是,有一些严重的错误(从 Xcode 6.2/iOS 8.2 开始)在从 nib 或 Storyboard 加载单元格时未正确/自动设置.
为了解决这个错误,我们需要将 preferredMaxLayoutWidth
设置为与标签在表格视图中显示后的最终宽度完全相等.实际上,我们希望在从 tableView:cellForRowAtIndexPath:
:
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
之所以单独添加这段代码是行不通的,是因为在tableView:cellForRowAtIndexPath:
中执行这3行代码时,我们使用的是每个标签的宽度来设置preferredMaxLayoutWidth
-- 但是,如果您此时检查标签的宽度,标签宽度与显示单元格及其子视图后的最终宽度完全不同.
此时我们如何使标签宽度准确,以便它们反映其最终宽度?以下是使这一切结合在一起的代码:
//tableView:cellForRowAtIndexPath: 内部,出列单元格后cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)cell.contentView.bounds = cell.boundscell.layoutIfNeeded()cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
好的,那我们在这里做什么?好吧,您会注意到添加了 3 行新代码.首先,我们需要设置这个表格视图单元格的宽度,使其与表格视图的实际宽度相匹配(这假设表格视图已经布局并且有它的最终宽度,应该是这种情况).我们实际上只是在早期使单元格宽度正确,因为表格视图最终会这样做.
您还会注意到我们使用 99999
作为高度.那是怎么回事?这是此处详细讨论的问题的简单解决方法,如果您的约束需要比当前更多的垂直空间单元格的 contentView 的高度,你会得到一个约束异常,它实际上并不表示任何真正的问题.此时单元格或其任何子视图的高度实际上并不重要,因为我们只关心获取每个标签的最终宽度.
接下来,我们通过将 contentView 的边界设置为等于单元格的边界来确保单元格的 contentView 与我们刚刚分配给单元格本身的大小相同.这是必要的,因为您创建的所有自动布局约束都与 contentView 相关,因此 contentView 必须具有正确的大小才能正确解决它们.只需手动设置单元格的大小不会自动调整 contentView 的大小以匹配.
最后,我们强制对单元格进行布局传递,这将使自动布局引擎解决您的约束并更新所有子视图的框架.由于细胞&contentView 现在在表格视图中具有与运行时相同的宽度,标签宽度也将正确,这意味着为每个标签设置的 preferredMaxLayoutWidth
将是准确的,并将导致标签在正确的时间,这当然意味着在表格视图中使用单元格时,标签的高度将被正确设置!
这绝对是 UIKit 中的一个 Apple 错误,我们现在必须解决(所以请向 Apple 提交错误报告,以便他们优先处理修复!).
最后一点:如果您的表格视图单元格的 contentView
宽度没有扩展表格视图的整个宽度,例如当有一个节索引显示在对.在这种情况下,您需要确保在设置单元格的宽度时手动考虑到这一点——您可能需要对这些值进行硬编码,例如:
让 cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidthcell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
In this iOS 8 app I'm creating, I have a tableview and I need them to be self resizing. I implemented it using Auto Layout and it works. Almost. Here's how it looks now.
There are 3 labels inside a cell. Main label which has the lorem ipsum text. Subtitle which has the string of numbers (Those are two separate labels. Might be confusing because they have the same color.) Then the third label with the small black text.
The first label resized itself correctly with no problem and the second label moves up and down accordingly. But the problem is with the third small label. As you can see, its not resizing itself to fit all the text.
Now there's a weird thing happening. I turn it landscape and here's it is.
Since there is space the label is displaying the entire text its supposed to. Fine. Then I turn it back to portrait.
Now the small label has resized itself to fit all its text but it overflows the cells boundaries. I tried making the cell bigger but it didn't work. Since this is self sizing cells, I don't think that's the correct way even.
I'm not getting any errors or even warning on my auto layout constraints either.
I have set these two lines of code in the viewDidLoad()
method.
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
Can anyone please tell me what I might be doing wrong here?
Since its difficult to answer just by looking at images and I don't have any more code to post beside the above snippet, I uploaded a runnable Xcode project demonstrating the issue here. (There are 2 custom cells. Basically its the same cell just the height is increased in the second one.)
I've been fiddling with auto layout constraints but I can't seem to get this working. Any help would be appreciated.
Thank you.
UPDATE:
With the help of this tutorial I found some helpful pointers. According to it, each subview should have constraints that pin all its sides and there should be constraints that goes from top to bottom which helps auto layout to calculate the height of the cell. In my original post, I had vertical spaces between each label so I think that's the reason auto layout couldn't calculate the proper height.
So I made some changes.
Now here's another weird part. When I first run it, the bottom label cropping issue is still there.
But if I rotate the device to landscape and turn it back to portrait, all the all the cells are resized properly to fit both labels!
Still can't figure out why this doesn't happen at first though. Updated Xcode project is here.
The issue here is with the multi-line labels' preferredMaxLayoutWidth
property. This is the property that tells the label when it should word wrap. It must be set correctly in order for each label's intrinsicContentSize
to have the correct height, which is ultimately what Auto Layout will be using to determine the cell's height.
Xcode 6 Interface Builder introduced a new option to have this property set to Automatic. Unfortunately, there are some serious bugs (as of Xcode 6.2/iOS 8.2) where this is not set correctly/automatically when loading a cell from a nib or Storyboard.
In order to work around this bug, we need to have the preferredMaxLayoutWidth
set to be exactly equal to the final width of the label once it is displayed in the table view. Effectively, we want to do the following before returning the cell from tableView:cellForRowAtIndexPath:
:
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
The reason that just adding this code alone doesn't work is because when these 3 lines of code execute in tableView:cellForRowAtIndexPath:
, we are using the width of each label to set the preferredMaxLayoutWidth
-- however, if you check the width of the labels at this point in time, the label width is totally different from what it will end up being once the cell is displayed and its subviews have been laid out.
How do we get the label widths to be accurate at this point, so that they reflect their final width? Here's the code that makes it all come together:
// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell
cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
OK, so what are we doing here? Well, you'll notice there are 3 new lines of code added. First, we need to set this table view cell's width so that it matches the actual width of the table view (this assumes the table view has already been laid out and has its final width, which should be the case). We're effectively just making the cell width correct early, since the table view is going to do this eventually.
You'll also notice that we're using 99999
for the height. What's that about? That is a simple workaround for the problem discussed in detail here, where if your constraints require more vertical space than the current height of the cell's contentView, you get a constraint exception that doesn't actually indicate any real problem. The height of the cell or any of its subviews doesn't actually matter at this point, because we only care about getting the final widths for each label.
Next, we make sure that the contentView of the cell has the same size as we just assigned to the cell itself, by setting the contentView's bounds to equal the cell's bounds. This is necessary because all of the auto layout constraints you have created are relative to the contentView, so the contentView must be the correct size in order for them to get solved correctly. Just setting the cell's size manually does not automatically size the contentView to match.
Finally, we force a layout pass on the cell, which will have the auto layout engine solve your constraints and update the frames of all the subviews. Since the cell & contentView now have the same widths they will at runtime in the table view, the label widths will also be correct, which means that the preferredMaxLayoutWidth
set to each label will be accurate and will cause the label to wrap at the right time, which of course means the labels' heights will be set correctly when the cell is used in the table view!
This is definitely an Apple bug in UIKit that we have to workaround for now (so please do file bug reports with Apple so they prioritize a fix!).
One final note: this workaround will run into trouble if your table view cell's contentView
width doesn't extend the full width of the table view, for example when there is a section index showing on the right. In this case, you'll need to make sure that you manually take this into account when setting the width of the cell -- you may need to hardcode these values, something like:
let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
这篇关于自调整大小的 UITableViewCell 中的多个 UILabel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!