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

时间:2016-04-13 01:50:49

标签: ios uitableview autolayout

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

我已经尝试过使用numberOfLines = 0,estimatedRowHeight,UITableViewAutomaticDimension,preferredMaxWidthLayout等,但在所有排列行为中 - 实际上对于所有 UITableViewCell样式 - 是否出现了UITableViewAutomaticDimension单元格高度计算将正确地考虑多行 textLabel (yay!),但错误地认为 detailTextlabel 最多是单行(nay!)。因此,具有多行detailTextLabel的单元格太短,因此单元格内容溢出单元格的顶部和底部。

enter image description here

我发布了一个快速测试应用,在GitHub here上显示此行为。添加额外的文本行很好 - 所有单元格样式的高度适当增加以适应 - 但添加额外的细节行不会改变单元格高度,并且很快会导致内容溢出;文本+细节本身正确布局,并且一起正确地居中在单元格的中间(因此在这种意义上,layoutSubviews正常工作),但整体单元格高度本身没有变化。

几乎看起来没有真正的顶级& cell.contentView和标签之间的底部约束,而是直接从(可能是多行)textLabel和(只有单行)detailTextLabel的高度计算单元格高度,然后所有内容都集中在中间单元格...再次,多行textLabel很好,我在textLabel和detailTextLabel之间没有任何不同,但只有前者(正确)调整单元格高度。

所以我的问题是,如果可以使用内置的UITableViewCell样式来可靠地显示多行 detailTextLabels ,或者它根本不可能而且您需要改为创建一个自定义子类? [或者,几乎等效地,不必覆盖子类中的layoutSubviews并手动重新连接所有约束]。

[2016年5月4日]结论:从iOS9开始,多行detailTextLabels无法按预期使用UITableViewAutomaticDimension;单元格将始终太短,文本/细节将溢出顶部和底部。您必须自己手动计算正确的单元格高度,或者创建和布局您自己的等效自定义UITableViewCell子类,或者(请参阅下面的答案)子类UITableViewCell并修复systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:以返回正确的高度[推荐]

5 个答案:

答案 0 :(得分:20)

进一步调查(参见UITableViewCellTest)表明,当启用 UITableViewAutomaticDimension 时,系统调用-systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:来计算单元格高度,这几乎忽略了高度detailTextLabel在其计算中(bug!?)。因此,对于 UITableViewCellStyleSubtitle ,单元格高度总是太短[单行 detailTextLabel 可能不会完全溢出单元格,但这仅仅是因为现有的上边距和下边距],对于 UITableViewCellStyleValue1 UITableViewCellStyleValue2 ,只要 detailTextLabel 更高(例如更多行),高度就会太短 textLabel 。这对于没有detailTextLabel的 UITableViewCellStyleDefault 来说都是没有实际意义的。

我的解决方案是继承并修复:

- (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

到目前为止,此修复程序似乎运行良好,并且让我成功使用内置的UITableViewCellStyles与多行文本和详细信息,在具有动态类型支持的自定义单元格中。这避免了在tableView:heightForRowAtIndexPath:中手动计算所需单元格高度的麻烦(和混乱),或者必须创建自定义单元格布局。

[(部分)在iOS11中固定]

Apple 终于修复了iOS11中的这个错误(但仅适用于UITableViewCellStyleSubtitle)。我已经更新了我的解决方案,只对11之前的设备应用了必要的修正(否则你最终会得到额外的空间顶部和底部!)。

答案 1 :(得分:4)

@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
}

答案 2 :(得分:2)

Swift 3

在阅读了各种答案之后,我使用了以下方法来获取详细文本标签UITableViewAutomaticDimension问题。仅使用带有标题标签的基本样式单元格,并将文本和详细文本视图的属性字符串用于。不要忘记将tableview单元格样式从Subtitle更改为Basic。

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()

答案 3 :(得分:1)

根据我的经验,内置单元格不支持自动调整大小限制,我认为最好的解决方案是创建一个自定义单元格,它真的需要几分钟而你不需要覆盖layoutSubview,真的很简单。 只需将IB中单元格的类型更改为自定义,拖动标签,设置约束(在IB中),设置行数,创建子类,将IB中的单元格类更改为子类,在子类和大部分工作, 我相信你可以在网上找到很多教程。

答案 4 :(得分:1)

看起来Apple已经在iOS 11中解决了这个错误。