自我调整大小的 UITableViewCell 内的多个 UILabels [英] Multiple UILabels inside a self sizing UITableViewCell

查看:27
本文介绍了自我调整大小的 UITableViewCell 内的多个 UILabels的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我创建的这个 iOS 8 应用程序中,我有一个 tableview,我需要它们自我调整大小.我使用自动布局实现了它并且它有效.几乎.这是它现在的样子.

一个单元格中有 3 个标签.具有 lorem ipsum 文本的主标签.带有数字字符串的副标题(这是两个单独的标签.可能会混淆,因为它们具有相同的颜色.)然后是带有黑色小文本的第三个标签.

第一个标签正确调整大小,没有问题,第二个标签相应地上下移动.但问题在于第三个小标签.如您所见,它不会调整自身大小以适应所有文本.

现在发生了一件奇怪的事情.我把它变成风景,就是这样.

由于有空间,标签显示了它应该显示的整个文本.美好的.然后我把它转回纵向.

现在小标签已调整大小以适合其所有文本,但它溢出了单元格边界.我尝试将单元格变大,但没有用.由于这是自定尺寸单元格,我什至认为这不是正确的方法.

我的自动布局约束也没有收到任何错误甚至警告.

我在viewDidLoad()方法中设置了这两行代码.

tableView.estimatedRowHeight = 100tableView.rowHeight = UITableViewAutomaticDimension

谁能告诉我我在这里可能做错了什么?

由于仅通过查看图像很难回答,而且除了上面的代码片段之外我没有更多代码可以发布,因此我上传了一个可运行的 Xcode 项目来演示该问题 .

解决方案

这里的问题是多行标签的 preferredMaxLayoutWidth 属性.这是告诉标签何时应该自动换行的属性.必须正确设置它才能使每个标签的 intrinsicContentSize 具有正确的高度,这最终是 Auto Layout 将用来确定单元格高度的.

Xcode 6 Interface Builder 引入了一个新选项,可以将此属性设置为 Automatic.不幸的是,有一些严重的错误(从 Xcode 6.2/iOS 8.2 开始),当从笔尖或故事板加载单元格时,这些错误没有正确/自动设置.

为了解决这个错误,我们需要将 preferredMaxLayoutWidth 设置为与标签在表格视图中显示后的最终宽度完全相等.实际上,我们希望在从 tableView:cellForRowAtIndexPath::

返回单元格之前执行以下操作

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

单独添加这段代码行不通的原因是,当这3行代码在tableView:cellForRowAtIndexPath:中执行时,我们使用每个标签的宽度来设置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 现在具有与它们在运行时在 table view 中相同的宽度,标签宽度也将是正确的,这意味着为每个标签设置的 preferredMaxLayoutWidth 将是准确的,并将导致标签在正确的时间,这当然意味着在表格视图中使用单元格时标签的高度将被正确设置!

这绝对是 UIKit 中的一个 Apple 错误,我们现在必须解决这个问题(所以请与 Apple 一起提交错误报告,以便他们优先考虑修复!).

最后一点:如果您的表格视图单元格的 contentView 宽度没有扩展表格视图的整个宽度,例如当有节索引显示在对.在这种情况下,您需要确保在设置单元格宽度时手动考虑到这一点——您可能需要对这些值进行硬编码,例如:

let 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.

  • I reduced the vertical space between labels to 0 and set the Vertical space constraints between top and middle labels and middle and bottom labels.
  • I added leading, top, trailing constraints to the top label.
  • Leading and trailing to the middle label.
  • Leading, bottom, trailing to the bottom label.

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 内的多个 UILabels的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆