UITableView dynamic cell heights are correct only after scrolling

I have a UITableView with a custom UITableViewCell defined in the storyboard using auto layout. A cell has several multi-line UILabels .

UITableView seems to correctly calculate cell heights, but for the first few cells, the height is not properly distributed between labels. After scrolling, everything works as expected (even from the original cells).

 - (void)viewDidLoad { [super viewDidLoad] // ... self.tableView.rowHeight = UITableViewAutomaticDimension; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"]; // ... // Set label.text for variable length string. return cell; } 

Is there something I can miss that leads to the fact that the car market will not be able to do its job the first few times?

I created a sample project that demonstrates this behavior.

Sample project: Top of table view from sample project on first load.Sample project: Same cells after scrolling down and back up.

+101
ios uitableview autolayout ios8 ios-autolayout
Sep 22 '14 at 9:48
source share
24 answers

I don't know if this is clearly documented or not, but adding [cell layoutIfNeeded] before returning the cell solves your problem.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"]; NSUInteger n1 = firstLabelWordCount[indexPath.row]; NSUInteger n2 = secondLabelWordCount[indexPath.row]; [cell setNumberOfWordsForFirstLabel:n1 secondLabel:n2]; [cell layoutIfNeeded]; // <- added return cell; } 
+125
Sep 22 '14 at 9:58
source share

This worked for me when other similar solutions failed:

 override func didMoveToSuperview() { super.didMoveToSuperview() layoutIfNeeded() } 

This seems like a real mistake, since I am very familiar with AutoLayout and how to use UITableViewAutomaticDimension, however, I still sometimes encounter this problem. I'm glad I finally found something that works as a workaround.

+34
Dec 12 '16 at 14:23
source share

Adding [cell layoutIfNeeded] to cellForRowAtIndexPath does not work for cells that initially scroll out of sight.

It does not precede it [cell setNeedsLayout] .

You still have to scroll through certain cells and return to the field of view so that they resize correctly.

This is rather unpleasant, as most developers have a dynamic type, AutoLayout and Self-Sizing Cells, which work fine - except for this unpleasant case. This error affects all of my "higher" table view controllers.

+20
May 12 '15 at 19:01
source share

I had the same experience in one of my projects.

Why is this happening?

A cell designed in a storyboard with some width for some device. For example, 400 pixels. For example, your label has the same width. When it loads from the storyboard, it has a width of 400 pixels.

Here is the problem:

tableView:heightForRowAtIndexPath: called before the location of the cells it is viewing.

So, he calculated the height for the label and cell with a width of 400 pixels. But you start the device with a screen, for example, 320 pixels. And this automatically calculated height is incorrect. Just because the layoutSubviews cell layoutSubviews happens after tableView:heightForRowAtIndexPath: Even if you set the preferredMaxLayoutWidth for your label manually in layoutSubviews , this will not help.

My decision:

1) Subclass UITableView and override dequeueReusableCellWithIdentifier:forIndexPath: Set the cell width equal to the width of the table and the power cell.

 - (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [super dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath]; CGRect cellFrame = cell.frame; cellFrame.size.width = self.frame.size.width; cell.frame = cellFrame; [cell layoutIfNeeded]; return cell; } 

2) Subclass of UITableViewCell . Set preferredMaxLayoutWidth manually for your labels in layoutSubviews . Also you need to manually layout the contentView , because it is not automatically the layout after changing the cell border (I do not know why, but it is)

 - (void)layoutSubviews { [super layoutSubviews]; [self.contentView layoutIfNeeded]; self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width; } 
+13
Sep 28 '15 at 21:54
source share

I have a similar problem, the first time the line height was not calculated, but after some scrolling or moving to another screen, and I will return to this screen, the lines are calculated. On the first boot, my goods are downloaded from the Internet, and on the second boot, my objects are downloaded first from Core Data and reloaded from the Internet, and I noticed that the line height is calculated when I reboot from the Internet. Therefore, I noticed that when tableView.reloadData () is called during the segue animation (the same problem with push and present segue), the row height was not calculated. Therefore, I hid tableview when initializing the view and placed an activity loader to prevent the ugly effect for the user, and I call tableView.reloadData after 300 ms, and now the problem is solved. I think this is a UIKit error, but this workaround does the trick.

I put the abstract (Swift 3.0) in the element load completion handler

 DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: { self.tableView.isHidden = false self.loader.stopAnimating() self.tableView.reloadData() }) 

This explains why for some people set reloadData to layoutSubviews, solving the problem

+9
Dec 26 '16 at 21:11
source share

In my case, the last row of the UILabel was truncated when the cell was displayed for the first time. This happened in a rather random way, and the only way to determine it correctly is to scroll the cell from the view and return it. I tried all possible solutions displayed so far (layoutIfNeeded..reloadData) but nothing worked for me. The trick was to set Auto Reset to the Minimuum Font Scale scale (0.5 for me). Try

+4
May 01 '15 at 13:36
source share

Add a constraint for all content to the user cell in the table view, then evaluate the row height of the table table and set the row height to automatic measurement using the viewdid load:

  override func viewDidLoad() { super.viewDidLoad() tableView.estimatedRowHeight = 70 tableView.rowHeight = UITableViewAutomaticDimension } 

To fix this bootstrap error, apply the layoutIfNeeded method using a custom table view cell:

 class CustomTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() self.layoutIfNeeded() // Initialization code } } 
+4
Aug 08 '16 at 16:14
source share

I tried most of the answers to this question and could not get any of them to work. The only functional solution I found was to add the following to my subclass of UITableViewController :

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) UIView.performWithoutAnimation { tableView.beginUpdates() tableView.endUpdates() } } 

A call to UIView.performWithoutAnimation , otherwise you will see an animation of the usual table view when the view controller loads.

+4
May 30 '17 at 19:44
source share

Setting preferredMaxLayoutWidth helps in my case. I added

 cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width 

in my code.

Also link to One line text takes two lines in UILabel and http://openradar.appspot.com/17799811 .

+2
Jun 09 '15 at 0:32
source share

I tried all the solutions on this page, but unchecking using the size classes and then checking it again, solved my problem.

Change: Unmarking size classes causes a lot of problems on the storyboard, so I tried a different solution. I populated my table view in my viewDidLoad and viewWillAppear controller. This solved my problem.

+2
Sep 25 '15 at 14:11
source share

Setting a screenshot for your essay None of these approaches worked for me, but I found that the shortcut has an explicit Preferred Width set in Interface Builder. Removing this (unchecking the "Explicitly" checkbox) and then using the UITableViewAutomaticDimension worked as expected.

+2
Feb 24 '17 at 18:28
source share

cell.layoutIfNeeded() inside cellForRowAt worked for me on ios 10 and ios 11, but not on ios 9.

to get this working also on ios 9, I call cell.layoutSubviews() and he cell.layoutSubviews() .

+2
Mar 28 '18 at 17:02
source share

I have a problem resizing the shortcut, so I only like to do chatTextLabel.text = chatMessage.message chatTextLabel? .UpdateConstraints () after setting the text

// full code

 func setContent() { chatTextLabel.text = chatMessage.message chatTextLabel?.updateConstraints() let labelTextWidth = (chatTextLabel?.intrinsicContentSize().width) ?? 0 let labelTextHeight = chatTextLabel?.intrinsicContentSize().height guard labelTextWidth < originWidth && labelTextHeight <= singleLineRowheight else { trailingConstraint?.constant = trailingConstant return } trailingConstraint?.constant = trailingConstant + (originWidth - labelTextWidth) } 
+1
May 20 '16 at
source share

In my case, I updated in another loop. Thus, the height of tableViewCell was updated after setting labelText. I deleted the asynchronous block.

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier:Identifier, for: indexPath) // Check your cycle if update cycle is same or not // DispatchQueue.main.async { cell.label.text = nil // } } 
+1
Apr 04 '17 at 5:52 on
source share

Just make sure that you do not set the label text in the delegate method 'willdisplaycell' in the table view. Set the label text in the delegate method 'cellForRowAtindexPath' to calculate the dynamic height.

Welcome:)

+1
May 25 '17 at 6:12
source share

The problem is that the original cells are loaded before we get a valid row height. The workaround is to make the table reload when the view appears.

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.tableView reloadData]; } 
+1
Nov 20 '17 at 13:44 on
source share

None of the above solutions worked for me, this magic recipe worked: call them in the following order:

tableView.reloadData()
tableView.layoutIfNeeded() tableView.beginUpdates() tableView.endUpdates()

my tableView data is populated from a web service, when I call back the connection, I write the above lines.

+1
Apr 03 '19 at 20:45
source share

In my case, the problem with the height of the cell occurs after loading the initial view of the table and a user action occurs (clicking on the button in the cell, which affects the change in cell height). I was not able to get the cell to change its height unless I did:

 [self.tableView reloadData]; 

I tried

 [cell layoutIfNeeded]; 

but it didn’t work.

0
Nov 04 '16 at 20:23
source share

In Swift 3. I had to call self.layoutIfNeeded () every time I update the text of a reused cell.

 import UIKit import SnapKit class CommentTableViewCell: UITableViewCell { static let reuseIdentifier = "CommentTableViewCell" var comment: Comment! { didSet { textLbl.attributedText = comment.attributedTextToDisplay() self.layoutIfNeeded() //This is a fix to make propper automatic dimentions (height). } } internal var textLbl = UILabel() override func layoutSubviews() { super.layoutSubviews() if textLbl.superview == nil { textLbl.numberOfLines = 0 textLbl.lineBreakMode = .byWordWrapping self.contentView.addSubview(textLbl) textLbl.snp.makeConstraints({ (make) in make.left.equalTo(contentView.snp.left).inset(10) make.right.equalTo(contentView.snp.right).inset(10) make.top.equalTo(contentView.snp.top).inset(10) make.bottom.equalTo(contentView.snp.bottom).inset(10) }) } } } 



 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let comment = comments[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: CommentTableViewCell.reuseIdentifier, for: indexPath) as! CommentTableViewCell cell.selectionStyle = .none cell.comment = comment return cell } 



 commentsTableView.rowHeight = UITableViewAutomaticDimension commentsTableView.estimatedRowHeight = 140 
0
Jan 13 '17 at 6:51
source share

None of the above solutions were implemented, but the following combination of sentences was used.

You have to add the following to viewDidLoad ().

 DispatchQueue.main.async { self.tableView.reloadData() self.tableView.setNeedsLayout() self.tableView.layoutIfNeeded() self.tableView.reloadData() } 

The above combination of reloadData, setNeedsLayout and layoutIfNeeded worked, but not any other. However, this may be specific to cells in the design. And yes, I had to call reloadData twice to get it working.

Also set the following to viewDidLoad

 tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = MyEstimatedHeight 

In tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

 cell.setNeedsLayout() cell.layoutIfNeeded() 
0
Apr 22 '17 at 15:17
source share

In my case, the presentation of the stack in the cell caused a problem. This seems like a mistake. As soon as I deleted it, the problem was resolved.

0
May 26 '17 at 10:53 a.m.
source share

For those still facing this problem, try this magic line

  tableview.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true) 

Just make sure you call this after filling the table with data, to be sure that section 0 and row 0 are always available, otherwise it will fail, but first you can check their availability, I hope this helps.

0
Jun 17 '19 at 6:24
source share
 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ // call the method dynamiclabelHeightForText } 

use the method described above that dynamically returns the height of a row. And assign the same dynamic height to the library used.

 -(int)dynamiclabelHeightForText:(NSString *)text :(int)width :(UIFont *)font { CGSize maximumLabelSize = CGSizeMake(width,2500); CGSize expectedLabelSize = [text sizeWithFont:font constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping]; return expectedLabelSize.height; } 

This code will help you find the dynamic height for displaying text on the label.

-one
Sep 22 '14 at 12:43 on
source share

I ran into this problem and fixed it by moving the initialization code of my view / label FROM tableView(willDisplay cell:) TO tableView(cellForRowAt:) .

-one
Jun 12 '17 at 3:18
source share



All Articles