自定义UITableViewCell中的多个UILabel
在我创build的这个iOS 8应用程序中,我有一个tableview,我需要它们自我resize。 我使用自动布局实现它,它的工作原理。 几乎。 这是现在的样子。
单元格内有3个标签。 有lorem ipsum文本的主要标签。 带有数字串的字幕(这是两个单独的标签,因为它们具有相同的颜色可能会令人困惑)。然后是带有小黑色文本的第三个标签。
第一个标签正确resize,没有问题,第二个标签相应地上下移动。 但问题在于第三个小标签。 正如你所看到的,它不会调整自己以适应所有的文本。
现在有一个奇怪的事情发生。 我把它变成风景,就是这样。
由于有空间标签正在显示其应该的整个文本。 精细。 然后我把它转回到肖像。
现在,小标签已经调整了自己的大小,以适应所有的文本,但它溢出了单元格边界。 我试图让这个单元格更大,但没有奏效。 由于这是自我大小细胞,我不认为这是正确的方式。
我没有得到任何错误,甚至在我的自动布局约束的警告。
我已经在viewDidLoad()
方法中设置了这两行代码。
tableView.estimatedRowHeight = 100 tableView.rowHeight = UITableViewAutomaticDimension
任何人都可以告诉我,我可能在这里做错了吗?
由于仅仅通过查看图片很难回答,而且我没有更多的代码来发布上面的代码片段,所以我上传了一个可运行的Xcode项目来演示这个问题。 (有2个自定义的单元格,基本上它的同一个单元格的高度在第二个增加。)
我一直摆弄自动布局约束,但我似乎无法得到这个工作。 任何帮助,将不胜感激。
谢谢。
更新:
在本教程的帮助下,我find了一些有用的指针。 据此,每个子视图都应该有一个约束,它的所有方面都应该有自上而下的约束,这有助于自动布局来计算单元的高度。 在我原来的文章,我有每个标签之间的垂直空间,所以我认为这是自动布局无法计算适当的高度的原因。
所以我做了一些改变。
- 我将标签之间的垂直空间缩小到0,并设置顶部和中间标签以及中间和底部标签之间的垂直空间约束。
- 我向顶部标签添加了领先的顶部尾随限制。
- 领先和尾随中间的标签。
- 领先,底部,尾随底部的标签。
现在这是另一个奇怪的部分。 当我第一次运行它,底部标签修剪问题仍然存在。
但是,如果我将设备旋转到横向并将其重新转换为纵向,则所有单元格都将正确resize以适应两个标签!
不过还是不明白为什么这一开始不会发生。 更新的Xcode项目在这里 。
这里的问题是多行标签的preferredMaxLayoutWidth
属性。 这是告诉标签何时应该换行的属性。 它必须正确设置,以便每个标签的intrinsicContentSize
具有正确的高度,这最终是自动布局将用于确定单元格的高度。
Xcode 6界面生成器引入了一个新的选项,将此属性设置为自动 。 不幸的是,有一些严重的错误(如Xcode 6.2 / iOS 8.2),当从nib或Storyboard加载单元格时,这些错误没有被正确/自动地设置。
为了解决这个错误,我们需要将preferredMaxLayoutWidth
设置为完全等于标签在表格视图中显示的最终宽度。 实际上,我们希望在从tableView:cellForRowAtIndexPath:
返回单元格之前执行以下操作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:
执行时,我们使用每个标签的宽度来设置preferredMaxLayoutWidth
– 但是,如果您检查宽度标签在这个时间点,标签的宽度是完全不同的,一旦单元格被显示,并且它的子视图已经被布置,它将会结束。
我们如何让标签的宽度在这一点上是准确的,以便它们反映它们的最终宽度? 以下是使所有内容相互融合的代码:
// 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)
好的,我们在这里做什么? 那么,你会注意到有3个新的代码行添加。 首先,我们需要设置这个表格视图单元格的宽度,以便它与表格视图的实际宽度相匹配(假设表格视图已经布置并且具有最终的宽度,应该是这种情况)。 我们实际上只是尽早地修正了单元格宽度,因为表格视图最终会这样做。
您还会注意到我们正在使用99999
作为高度。 那是什么 对于这里详细讨论的问题,这是一个简单的解决方法,如果您的约束需要比单元格的contentView的当前高度更多的垂直空间,则会得到一个约束exception,但实际上并不表示任何实际问题。 单元格或其任何子视图的高度在这一点上并不重要,因为我们只关心如何获得每个标签的最终宽度。
接下来,我们确保单元格的contentView与我们刚分配给单元格的大小相同,方法是将contentView的边界设置为等于单元格的边界。 这是必要的,因为您创build的所有自动布局约束都是相对于contentView的,所以contentView必须是正确的大小才能正确解决。 手动设置单元格的大小不会自动调整contentView的大小以匹配。
最后,我们强制在单元格上进行布局传递,这将使自动布局引擎解决您的约束,并更新所有子视图的框架。 由于cell&contentView现在在运行时具有相同的宽度,因此标签宽度也是正确的,这意味着设置到每个标签的preferredMaxLayoutWidth
将是准确的,并且会导致标签在正确的时间,这当然意味着当单元格在桌子视图中使用时,标签的高度将被正确设置!
这肯定是UIKit中的一个苹果bug,我们现在必须解决这个问题(所以请给苹果提供bug报告 ,让他们优先考虑修补程序!)。
最后一点:如果您的表视图单元格的contentView
宽度不能扩展表视图的整个宽度,例如当右侧显示一个区段索引时,此解决方法将会遇到麻烦。 在这种情况下,您需要确保在设置单元格的宽度时手动将其考虑在内 – 您可能需要对这些值进行硬编码,如下所示:
let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
我遇到了同样的问题,我发现了一个简单的解决scheme来解决它。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // dequeue cell... // do autolayout staffs...or if the autolayout rule has been set in xib, do nothing [cell layoutIfNeeded]; return cell; }
自行筛选效果很好。 在我的代码中,我垂直放置了两个标签,它们都是dynamic高度。 单元格的高度正确设置为包含两个标签。
假设你没有像其他人所build议的那样约束你的错误,这个问题似乎源于使用UILabel,它允许多行与UITableViewCellAccessory结合使用。 当iOS对单元格进行布局并确定高度时,它不会考虑由于此附件而发生的宽度偏移更改,并且会在您不期望的地方截断。
假设你想让UILabel扩展内容视图的全部宽度,我写了一个方法来修复所有的字体大小
-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell { float offset = 0; switch ([cell accessoryType]) { case UITableViewCellAccessoryCheckmark: offset = 39.0; break; case UITableViewCellAccessoryDetailButton: offset = 47.0; break; case UITableViewCellAccessoryDetailDisclosureButton: offset = 67.0; break; case UITableViewCellAccessoryDisclosureIndicator: offset = 33.0; break; case UITableViewCellAccessoryNone: offset = 0; break; } [label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8]; }
简单地把这个放在你的cellForRowAtIndexPath中
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { #Setup the cell ... // Fix layout with accessory view [self fixWidth:[cell label] forCell:cell]; return cell; }
对于任何要多行的标签,都要适当调整宽度,然后重新计算适当的高度。 这也适用于dynamic字体大小。
就像smileyborg所提到的,如果你不使用contentView的全部宽度,你可以引用约束条件,并从宽度中减去它们。
编辑:我以前是在单元上运行“layoutIfNeeded”,但这是造成性能问题,似乎并不需要。 删除它并没有给我造成任何问题。
这里有两个问题。
1 /现在,使用Visual Format Language,你的单元格的垂直约束可以这样翻译:
Cell: "V:|-(10)-[nameLabel]-(67)-|"
然后,你设置第二组约束:
Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]"
这两组限制条件不能很好地混合在一起,并且会随着第二个问题而显示出模糊性。
2 /出于某些原因, actionsLabel
在启动应用程序时被限制为一行。 然后,当您将设备旋转到横向模式时, actionsLabel
接受显示两行或更多行。 然后,当您将设备旋转回肖像模式时, actionsLabel
会一直显示两行或更多行。 但是,由于actionsLabel
并不是您单元的高度限制的一部分,所以它与您单元格的边界重叠。
如果你想解决所有这些问题,我build议先从头开始重build你的xib文件。 这可以治愈你的行为actionsLabel
奇怪的行为(只有当你旋转你的设备时,两行或更多)。
然后,你将不得不像这样定义你的约束:
Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|"
当然,您可以为标签定义其他最小高度约束(>=21)
。 以同样的方式,您的底部边距可以设置为另一个值-(10)-
。
附录
为了回答你的问题,我在.xib文件中创build了一个带有先前约束模式的简单项目。 下面的图片可以帮助您build立自己的约束。
我尝试了“fogisland”的非常简单和优雅的解决scheme – 它没有工作。 幸运的是,我发现另外一条线使它在方向上工作。 只要告诉系统你不仅build议一个新的布局(layoutIfNeeded),你明确地要求它(setNeedLayout)
cell.setNeedsLayout() cell.layoutIfNeeded() return cell
- Objective-C和Swift URL编码
- 自定义初始值设定项的Swift枚举失去了rawValue初始值设定项
- 如何在Swift中拆分string
- 使用select器'touchesBegan:withEvent:'具有不兼容types'(NSSet,UIEvent) – >()'的覆盖方法
- 如何解决“模糊使用”与Swift #selector语法编译错误?
- 泄漏仪器不显示时如何debugging内存泄漏?
- 使用@testable时,'模块未被编译进行testing'
- 如何在Swift中将正确位置的元素插入到已sorting的数组中?
- 将CocoaPods更新到0.36.x及更高版本后,如何在Bridging-Header.h中引用头文件?