Autolayout ignores multi-line detailTextLabel when calculating UITableViewCell height (all styles)
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!).
Multiline UILabel in a UITableViewCell giving the wrong height with autoLayout in iOS8
Your code is probably correct.i just added this two line (not necessary to say. from the look of your screenshots you already added!)
cell. bodyLabel.numberOfLines = 0;
cell. bodyLabel.lineBreakMode = NSLineBreakByWordWrapping;
Its seems that your problem lay in your user interface. This is the user interface that i used for UITableViewCell and it worked perfectly fine
UPDATE!
just add this to your viewDidLoad [self.view layoutIfNeeded]; [self.view setNeedsLayout];
in viewDidLoad the frame of user interface is being used and you need to update the constraint and view to use the device screen frame The sequence of layoutIfNeeded and setNeedsLayout is important. you need to first call layoutIfNeeded.
UITableViewCell height auto layout not working on iOS 10
This configuration from your viewDidLoad
:
tableView.estimatedRowHeight = UITableViewAutomaticDimension
tableView.rowHeight = UITableViewAutomaticDimension
...works only on iOS 11. It is an iOS 11 innovation and does not apply to earlier systems.
For iOS 10, you must supply an actual estimatedRowHeight
value (such as 60
) to turn on automatic variable row height calculation.
Self-Sizing (Dynamic Height) Cells in iOS 8 - Possible without Custom UITableViewCell?
I just tried this in iOS 10/XCode 8 (same results in iOS 9/XCode 7) with the different cell types and it looks like it's possible ONLY for the textLabel and not for the detailTextLabel.
(basically repeating the issue that the OP mentioned)
ViewController code that sets some text alternately on detailTextLabel and textLabel.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if indexPath.row % 2 == 0 {
cell.textLabel?.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
cell.detailTextLabel?.text = "<- textLabel"
} else {
cell.textLabel?.text = "detailTextLabel ->"
cell.detailTextLabel?.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
}
Make sure you set the textLabel and textDetailLabel's line property to 0 and here are the results.
Basic Cell
Right Detail Cell
Left Detail Cell
Subtitle Cell
I'll report this as a bug.
UITableViewAutomaticDimension applied on subtitle table view cell not working properly
Check this Modify code :-
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")
if (cell == nil)
{
cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "Cell")
cell?.selectionStyle = .None
cell?.detailTextLabel?.font = _SETREGULARFONT_KARLA(IS_IPAD ? 19:15)
cell?.textLabel?.font = _SETBOLDFONT_KARLA(IS_IPAD ? 17:13)
cell?.detailTextLabel?.textColor = getColorFromRGB(120, G: 132, B: 158, A: 1.0)
cell?.detailTextLabel?.numberOfLines = 0
cell?.detailTextLabel?.lineBreakMode = .ByTruncatingTail
cell?.selectionStyle = .None
}
cell?.textLabel?.text = "Testing" + "\(indexPath.row)"
cell?.detailTextLabel?.text = "tesfhjdsfskdfhjkfhdjskfhsjkdfhdjsxdfgfjljkgjfklgfhsdjfhjkdshfdjskfhdjfjkfhafyhdsifjhdjksfbshdjkfkhdksjfhdjsfyhds8ufdhsfjkhdsfjhdfudsiufhdsfh"
cell?.imageView?.image = UIImage(named: "new_temp")
return cell!
}
Please remove the HeightForRowAtIndex datasource Method.
ios8 autolayout: BOTH multi-line(maybe 0 line) label
Cells that are loaded from a storyboard don't start out with the right initial width.
This is why the label isn't sized properly the first time, but appears correctly once you (reload the data, or) scroll the cell off-screen, then on-screen.
Since the cell width is initially incorrect, the label ends up using a preferredMaxLayoutWidth
which is wider than the table. (The label thought it had more room to fit everything on one line.)
The solution which worked for me was to make sure my (subclassed) cell's width matched the tableView's width:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
[cell adjustSizeToMatchWidth:CGRectGetWidth(self.tableView.frame)];
[self configureCell:cell forRowAtIndexPath:indexPath];
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
In TableViewCell.m:
- (void)adjustSizeToMatchWidth:(CGFloat)width
{
// Workaround for visible cells not laid out properly since their layout was
// based on a different (initial) width from the tableView.
CGRect rect = self.frame;
rect.size.width = width;
self.frame = rect;
// Workaround for initial cell height less than auto layout required height.
rect = self.contentView.bounds;
rect.size.height = 99999.0;
rect.size.width = 99999.0;
self.contentView.bounds = rect;
}
I'd also recommend checking out smileyborg's excellent answer about self-sizing cells, along with his sample code. It's what tipped me off to the solution, when I bumped into the same issue you are having.
Related Topics
Uilabel and Uitextview Line Breaks Don't Match
iOS - Another Thread Needs to Send Reloaddata to the Mainthread
Dequeuereusablecellwithidentifier:Forindexpath: VS Dequeuereusablecellwithidentifier:
Displaying Youtube Video Ads with Youtube Iframe
Duplicate Interface Definition for Class 'Gtmhttpuploadfetcher'
How to Change the Height of Uitextfield in Uialertcontroller in Swift
Is This a Bug with Mkmapkitdelegate Mapview:Didupdateuserlocation
Memory Leak with "Libbacktracerecording.Dylib" in React Native iOS Application
Urlsessiondelegate Function Not Being Called
Avassetresourceloaderdelegate Methods Not Working on Device
Why Don't My Views Show Up in Storyboards and Are Greyed Out in the Left Pane
How to Detect Fullscreen Mode Using Avplayerviewcontroller in Swift
Private VS. Fileprivate on Declaring Global Variables/Consts in Swift3
iOS How to Use Uiapplication Launchapplicationwithidentifier Which Is in Private APIs
Drawing Rounded Rect in Core Graphics
Recording from Remoteio: Resulting .Caf Is Pitch Shifted Slower + Distorted