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

So, I'm trying to use the inline styles of UITableViewCell - specifically UITableViewCellStyleSubtitle - with a (one) line textLabel but a multi-line detailTextLabel . But the (automatically) calculated cell height is constantly too short and seems to ignore that there are more than 1 row of parts.

I tried using numberOfLines = 0, approxRowHeight, UITableViewAutomaticDimension, preferredMaxWidthLayout, etc., but in all permutations the behavior is valid for all UITableViewCell styles - it looks like the calculation of the cell height of the UITableViewAutomaticDimension correctly takes into account the multi-line textLabel (yay!), But incorrectly that detailTextlabel is not more than one line (no!). Therefore, cells with a multiline TextLabel part are too short, and therefore the contents of the cell spills over the top and bottom of the cell.

enter image description here

I posted a quick testing application showing this behavior on GitHub here . Adding additional lines of text in order - all cell styles increase accordingly in height to accommodate - but adding additional lines of detail does nothing to change the height of the cell and quickly causes the contents to spill; the text + part themselves are laid out correctly and are correctly centered together in the middle of the cell (so in this sense layoutSubviews works correctly), but the overall height of the cell itself does not change.

It looks like there are no actual upper and lower limits between cell.contentView and labels, and instead the cell height is calculated directly from the height of the (possibly multi-line) textLabel and (only one-line) detailTextLabel, and then everything is centered in the middle of the cell ... Again, a multi-line textLabel is fine, and I don't do anything else between textLabel and detailTextLabel, but only the first (correctly) sets the cell height.

So my question is, is it possible to use UITableViewCell's built-in styles to reliably display multi-line detailTextLabels , or is it just not possible and you need to create your own subclass? [or, almost equivalently, without having to override layoutSubviews in a subclass and manually reset all restrictions).


[May 4, 2016]. Conclusion: with multi-line detail, iOS9TextLabels do not work as expected with the UITableViewAutomaticDimension; the cell will be too short, and the text / part will spill from above and below. Either you must manually calculate the correct cell height yourself, or create and place your own equivalent custom subclass of UITableViewCell or (see My answer below) a subclass of UITableViewCell and fix systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to return the correct height [recommended]

+8
ios uitableview autolayout
source share
5 answers

Further research (see UITableViewCellTest ) shows that when you turn on the UITableViewAutomaticDimension, the system calls -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to calculate the height of the cell and that this largely ignores the height of the TextLabel part in its calculation (error !?). As a result, for a UITableViewCellStyleSubtitle, the cell height will always be too short [a single-line detailTextLabel may not completely spill over the cell, but only because of the existing upper and lower fields] and for UITableViewCellStyleValue1 or UITableViewCellStyleValue2 the height will be too short if it is longer than the detailTextLabel ) than textLabel . This is all a moot point for a UITableViewCellStyleDefault that has no detail. TextLabel

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 highlight the full subclass here: MultilineTableViewCell

So far, this fix has worked well, and I have successfully used the built-in UITableViewCellStyles with text and multiline details, in self-calibration cells with dynamic type support. This avoids the problem (and clutter) of manually calculating the required cell heights in tableView:heightForRowAtIndexPath: or creating your own cell layouts.

[FIXED IN iOS11]

Apple finally fixed this bug in iOS11. I updated my solution to only apply patches to devices up to 11 (otherwise, you will have extra space above and below your cell!).

+18
source share

@tiritea 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
source share

In my experience, the built-in cells do not support auto-resizing with restrictions, 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 cell type in IB to a custom one, drag the label, set limits (in IB), set the number of rows, create a subclass, change the cell class in IB to your subclass, create an output in the subclass and that is most of the work, I’m sure that in There are many tutorials online.

+1
source share

It looks like Apple solved this bug in iOS 11.

+1
source share

Swift 3

After reading the various answers, I used the following method to get the detailed text label of the UITableViewAutomaticDimension. Use the Basic style cell only with the name label and use the attribute string for text and text viewing. Remember to change the style of the tableview cell 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 the following lines to viewdidload

  YourTableView.estimatedRowHeight = UITableViewAutomaticDimension YourTableView.rowHeight = UITableViewAutomaticDimension YourTableView.setNeedsLayout() YourTableView.layoutIfNeeded() 
+1
source share

All Articles