Autolayout在计算UITableViewCell高度时忽略多行的detailTextLabel(所有样式)

所以我试图使用内置的UITableViewCell样式 – 特别是UITableViewCellStyleSubtitle – 与单行textLabel,多行的 detailTextLabel 。 但(自动)计算的单元格高度始终太短,似乎忽略了多于一行的细节。

我已经尝试使用numberOfLines = 0,estimatedRowHeight,UITableViewAutomaticDimension,preferredMaxWidthLayout等,但在所有的排列行为 – 的确是所有的UITableViewCell样式 – 看来UITableViewAutomaticDimension单元格高度计算将正确地占多行textLabel (耶! ),但错误地认为detailTextlabel最多只有一行( 不可 !)。 因此,具有多行detailTextLabel的单元格太短,因此单元格内容溢出单元格的顶部和底部。

在这里输入图像说明

我已经发布了一个快速testing应用程序,在GitHub上显示这种行为。 添加额外的文本行很好 – 所有的单元格样式适当增加高度以适应 – 但添加额外的细节线没有任何改变单元格的高度,并迅速导致内容溢出; 文本+细节本身是正确布置的,并且正确地集中在单元格的中间(所以在这个意义上layoutSubviews正常工作),但是整个单元格高度本身并没有改变。

它几乎看起来像cell.contentView和标签之间没有实际的顶部和底部约束,而是直接从(可能是多行)textLabel和(只有单行)detailTextLabel的高度计算单元格高度,然后一切都集中在单元格的中间…再次,多行textLabel是好的,我在textLabel和detailTextLabel之间没有什么不同,但只有前者(正确)调整单元格的高度。

所以我的问题是,如果有可能使用内置的UITableViewCell样式来可靠地显示多行的 detailTextLabels ,或者这是不可能的,你需要创build一个自定义的子类吗? [或者,几乎等同于,不必重写子类中的layoutSubviews并手动重新连接所有约束]。


[2016年5月4日]结论:截至iOS9多行detailTextLabels不按预期与UITableViewAutomaticDimension工作; 单元格将一直太短,文本/细节将溢出顶部和底部。 要么你必须自己手动计算正确的单元格高度,或创build和布局自己的等价自定义UITableViewCell子类,或(请参阅我的答案下面)子类UITableViewCell和修复systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:返回正确的高度[推荐]

进一步的调查(请参阅UITableViewCellTest )指出,启用UITableViewAutomaticDimension时,系统调用-systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:来计算单元格高度,这几乎忽略了其计算中的detailTextLabel的高度(错误!?)。 因此,对于UITableViewCellStyleSubtitle ,单元格高度总是会太短[单行detailTextLabel可能不会溢出单元格,但这只是因为现有的顶部和底部边距],而对于UITableViewCellStyleValue1UITableViewCellStyleValue2而言 ,高度只要detailTextLabeltextLabel更高(比如更多的行) 就会太短 。 对于没有detailTextLabel的UITableViewCellStyleDefault来说 ,这是一个有争议的问题。

我的解决scheme是inheritance和修复:

 - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority { // Bug finally fixed in iOS 11 if ([UIDevice.currentDevice.systemVersion compare:@"11" options:NSNumericSearch] != NSOrderedAscending) { return [super systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; } [self layoutIfNeeded]; CGSize size = [super systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; CGFloat detailHeight = CGRectGetHeight(self.detailTextLabel.frame); if (detailHeight) { // if no detailTextLabel (eg style = Default) then no adjustment necessary // Determine UITableViewCellStyle by looking at textLabel vs detailTextLabel layout if (CGRectGetMinX(self.detailTextLabel.frame) > CGRectGetMinX(self.textLabel.frame)) { // style = Value1 or Value2 CGFloat textHeight = CGRectGetHeight(self.textLabel.frame); // If detailTextLabel taller than textLabel then add difference to cell height if (detailHeight > textHeight) size.height += detailHeight - textHeight; } else { // style = Subtitle, so always add subtitle height size.height += detailHeight; } } return size; } 

而在视图控制器中:

 - (void)viewDidLoad { [super viewDidLoad]; self.tableView.estimatedRowHeight = 44.0; self.tableView.rowHeight = UITableViewAutomaticDimension; } 

您可以从这里拉出完整的子类: MultilineTableViewCell

到目前为止,这个修补程序看起来工作得很好,并且让我在具有dynamictypes支持的自定义单元格中成功使用带有多行文本和细节的内置UITableViewCellStyles。 这样可以避免在tableView:heightForRowAtIndexPath:中手动计算所需单元格高度的麻烦(和混乱),或者必须创build自定义单元格布局。

[固定在iOS11]

苹果终于在iOS11中修复了这个bug。 我已经更新了我的解决scheme,仅对11之前的设备进行了必要的更正(否则,您将最终得到额外的空间,您的单元格的顶部和底部!)。

@tiritea在Swift 3中的答案(再次感谢!:D)

 // When UITableViewAutomaticDimension is enabled the system calls // -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to calculate the cell height. // Unfortunately, it ignores the height of the detailTextLabel in its computation (bug !?). // As a result, for UITableViewCellStyleSubtitle the cell height is always going to be too short. // So we override to include detailTextLabel height. // Credit: http://stackoverflow.com/a/37016869/467588 override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { self.layoutIfNeeded() var size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority) if let textLabel = self.textLabel, let detailTextLabel = self.detailTextLabel { let detailHeight = detailTextLabel.frame.size.height if detailTextLabel.frame.origin.x > textLabel.frame.origin.x { // style = Value1 or Value2 let textHeight = textLabel.frame.size.height if (detailHeight > textHeight) { size.height += detailHeight - textHeight } } else { // style = Subtitle, so always add subtitle height size.height += detailHeight } } return size } 

根据我的经验,内置的单元格不支持带约束的自动resize,我认为最好的解决scheme是创build一个自定义单元格,它真的需要几分钟的时间,而且您不需要重写layoutSubview,它非常简单。 只需将IB中单元格的types更改为自定义,拖动标签,设置约束(在IB中),设置行数,创build子类,将IB中的单元格类更改为您的子类,在子类,这是大部分的工作,我相信网上有很多的教程,你可以遵循。

Swift 3

阅读各种答案后,我用下面的方法获取详细文本标签UITableViewAutomaticDimension问题。 使用只有标题标签的基本样式单元格,并将文本和详细文本视图的属性string使用。 不要忘记将tableview单元格样式从“字幕”更改为“基本”。

 func makeAttributedString(title: String, subtitle: String) -> NSAttributedString { let titleAttributes = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: .headline), NSForegroundColorAttributeName: UIColor.purple] let subtitleAttributes = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: .subheadline)] let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes) let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes) titleString.append(subtitleString) return titleString } 

如何在cellforrowatindexpath中使用

 cell.textLabel?.attributedText = makeAttributedString(title: "Your Title", subtitle: "Your detail text label text here") 

在viewdidload中添加以下行

  YourTableView.estimatedRowHeight = UITableViewAutomaticDimension YourTableView.rowHeight = UITableViewAutomaticDimension YourTableView.setNeedsLayout() YourTableView.layoutIfNeeded() 

看起来苹果已经解决了iOS 11中的这个bug。