Autolayout ignores multi-line detailTextLabel when calculating UITableViewCell height (all styles)

╄→гoц情女王★ 提交于 2019-11-27 03:21:08

问题


So I'm trying to use the built-in UITableViewCell styles - specifically UITableViewCellStyleSubtitle - with a (single) line textLabel but multiline detailTextLabel. But the (auto) calculated cell height is consistently too short, and appears to ignore that there is more than 1 line of detail.

I've tried using numberOfLines=0, estimatedRowHeight, UITableViewAutomaticDimension, preferredMaxWidthLayout, etc, but in all the permutations the behavior - indeed for all the UITableViewCell styles - is it appears the UITableViewAutomaticDimension cell height calculation will correctly account for a multiline textLabel (yay!), but incorrectly assumes the detailTextlabel is at most single line (nay!). Consequently, cells with a multiline detailTextLabel are too short, and hence the cell content spills over the top and bottom of the cell.

I've posted a quick test app showing this behavior on GitHub here. Adding additional lines of text is fine - all the cell styles appropriately increase in height to accommodate - but adding additional lines of detail does nothing to change the cell height, and quickly causes the content to spill over; the text+detail are themselves laid out correctly, and together centered correctly over the middle of the cell (so in that sense layoutSubviews is working correctly), but the overall cell height itself is unchanged.

It almost seems like there are no actual top & bottom constraints between the cell.contentView and the labels, and instead the cell height is being calculated directly from the height of the (possibly multi-line) textLabel and (only single-line) detailTextLabel, and then everything is centered over the middle of the cell... Again, multiline textLabel is fine, and I'm doing nothing different between the textLabel and detailTextLabel, but only the former (correctly) adjusts the cell height.

So my question is, if it is possible to use the built-in UITableViewCell styles to reliably display multiline detailTextLabels, or is it simply not possible and you need to create a custom subclass instead? [or, almost equivalently, without having to override layoutSubviews in a subclass and rewire all the constraints manually].


[4 May 2016] Conclusion: as of iOS9 multi-line detailTextLabels dont work as expected with UITableViewAutomaticDimension; the cell will be consistently too short and the text/detail will spill over the top and bottom. Either you must manually compute the correct cell height yourself, or create and layout your own equivalent custom UITableViewCell subclass, or (see my answer below) subclass UITableViewCell and fix systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to return the correct height [recommended]


回答1:


Further investigations (see UITableViewCellTest) indicate that when UITableViewAutomaticDimension is enabled the system calls -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to calculate the cell height, and that this pretty much 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 [a single-line detailTextLabel may not quite spill over the cell, but that's only because of the existing top and bottom margins], and for UITableViewCellStyleValue1 or UITableViewCellStyleValue2 the height will be too short whenever the detailTextLabel is taller (eg more lines) than the textLabel. This is all a moot point for UITableViewCellStyleDefault which has no detailTextLabel.

My solution was to subclass and fix with:

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

And in the view controller:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 44.0;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}

You can pull the full subclass from here: MultilineTableViewCell

So far this fix appears to work well, and has let me successfully use the built-in UITableViewCellStyles with multiline text and details, in self-sizing cells with dynamic type support. This avoids the trouble (and mess) of manually computing the desired cell heights in tableView:heightForRowAtIndexPath:, or having to create custom cell layouts.

[(PARTLY)FIXED IN iOS11]

Apple finally fixed this bug in iOS11 (but apparantly only for UITableViewCellStyleSubtitle). I've updated my solution to only apply the necessary correction to pre-11 devices (otherwise you'll end up with extra space top and bottom of your cell!).




回答2:


@tiritea 's answer in Swift 3 (Thanks again! :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
}



回答3:


It looks like Apple has resolved this bug in iOS 11.




回答4:


Swift 3

After reading various answers, I have used following method for get ride of detail text label UITableViewAutomaticDimension issue . Use Basic style cell with title label only and use attributed string for Text and detail text view. Don't forget to Change tableview cell style from Subtitle to 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
}

How to use in cellforrowatindexpath

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

Add Following lines in viewdidload

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



回答5:


From my experience the built in cells don't support auto resize with constraints, I think the best solution is to create a custom cell, it really takes a couple of minutes and you don't need to override layoutSubview, it is really simple . Just change the type of the cell in the IB to custom, drag a label , set constraints (in the IB), set number of rows , create a subclass, change the cells class in the IB to your subclass, create an outlet in the subclass and that's most of the work, I am sure there are a lot of tutorials on the net you can follow.



来源:https://stackoverflow.com/questions/36587126/autolayout-ignores-multi-line-detailtextlabel-when-calculating-uitableviewcell-h

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!