一些滚动后,UITableViewdynamic单元格高度才会正确

我有一个UITableView自定义UITableViewCell使用自动布局在故事板中定义。 单元格有几个多行UILabels

UITableView似乎正确地计算单元格高度,但对于前几个单元格高度没有正确划分标签之间。 滚动一下后,一切都按预期工作(即使是最初不正确的单元格)。

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

有什么我可能会失踪,这是导致汽车布局不能够在前几次工作?

我已经创build了一个演示这种行为的示例项目 。

示例项目:第一次加载时,来自示例项目的表视图的顶部。示例项目:向下滚动并备份后的单元格相同。

我不知道这是清楚的logging或不,但在返回单元格之前添加[cell layoutIfNeeded]解决您的问题。

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

cellForRowAtIndexPath中添加[cell layoutIfNeeded]不适用于最初滚动到视野外的单元格。

也没有用[cell setNeedsLayout]预处理它。

您仍然需要将某些单元格向外滚动并返回到视图中才能正确resize。

这是非常令人沮丧的,因为大多数开发者都有dynamictypes,自动布局和自我大小单元正常工作 – 除了这个烦人的情况。 这个bug会影响我所有的“更高”的表格视图控制器。

当其他类似的解决scheme没有时,这对我有用:

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

这似乎是一个实际的错误,因为我非常熟悉AutoLayout和如何使用UITableViewAutomaticDimension,但是我仍然偶尔遇到这个问题。 我很高兴终于find了解决方法。

我的一个项目也有同样的经历。

为什么会发生?

在Storyboard中为某些设备devise了一些宽度的单元。 例如400px。 例如你的标签有相同的宽度。 当它从故事板加载它有宽度400px。

这是一个问题:

tableView:heightForRowAtIndexPath:在单元布局之前调用它的子视图。

所以它计算宽度为400px的标签和单元格的高度。 但是你在屏幕上运行设备,例如320px。 这个自动计算的高度是不正确的。 仅仅因为单元格的layoutSubviews仅在tableView:heightForRowAtIndexPath:之后发生tableView:heightForRowAtIndexPath:即使您在layoutSubviews手动为label设置preferredMaxLayoutWidth ,也layoutSubviews

我的解决scheme

1)子类UITableView并重写dequeueReusableCellWithIdentifier:forIndexPath: 设置单元格宽度等于表格宽度和强制单元格的布局。

 - (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)子类UITableViewCell 。 在layoutSubviews为您的标签手动设置preferredMaxLayoutWidth 。 此外,你需要手动布局contentView ,因为它不会自动布局后单元格框架(我不知道为什么,但它是)

 - (void)layoutSubviews { [super layoutSubviews]; [self.contentView layoutIfNeeded]; self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width; } 

我有一个类似的问题,在第一次加载,行高不计算,但经过一些滚动或转到另一个屏幕,我回到这个屏幕行计算。 在第一次加载时,我的物品从互联网加载,在第二次加载时,我的物品首先从核心数据加载,然后从互联网上重新加载,我注意到行高从互联网重新加载计算。 所以我注意到,当在segueanimation中调用tableView.reloadData()(与push和present segue相同的问题)时,行高不计算。 所以我隐藏tableview在视图初始化,并把一个活动加载器,以防止对用户的丑陋的影响,我在300ms后调用tableView.reloadData现在问题得到解决。 我认为这是一个UIKit的错误,但是这个解决方法使窍门。

我将这些行(Swift 3.0)放在我的项目加载完成处理程序中

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

这解释了为什么对于一些人来说,在layoutSubviews中放一个reloadData来解决这个问题

在我的情况下,UILabel的最后一行在单元格首次显示时被截断。 它发生得相当随机,正确resize的唯一方法是将单元格从视图中滚动并将其恢复。 我尝试了迄今为止显示的所有可能的解决scheme(layoutIfNeeded..reloadData),但没有为我工作。 诀窍是将“Autoshrink”设置为Minimuum Font Scale (0.5)。 试一试

为表格视图自定义单元格中的所有内容添加约束,然后估算表格视图行高度并将行高度设置为自动维度,并使用viewdid加载:

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

要修复初始加载问题,请在自定义表格视图单元格中应用layoutIfNeeded方法:

 class CustomTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() self.layoutIfNeeded() // Initialization code } } 

设置preferredMaxLayoutWidth有助于我的情况。 我补充说

 cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width 

在我的代码。

另请参阅单行文本在UILabel和http://openradar.appspot.com/17799811中; 占用两行 。

我尝试了这个页面中的所有解决scheme,但取消了使用大小类,然后再次检查解决了我的问题。

编辑:取消select大小类别导致故事板上的很多问题,所以我尝试了另一种解决scheme。 我在视图控制器的ViewDidLoadViewWillAppear方法中填充tableView。 这解决了我的问题。

我已经尝试了这个问题的大部分答案,并没有得到任何工作。 我发现唯一的function解决scheme是添加以下到我的UITableViewController子类:

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

UIView.performWithoutAnimation调用是必需的,否则当视图控制器加载时,您将看到普通的表视图animation。

为你的诠释截图截图 对于我来说,这些方法都行不通,但是我发现标签在界面生成器中有一个明确的Preferred Width 。 删除(取消选中“显式”),然后使用UITableViewAutomaticDimension按预期工作。

我有调整标签的问题,所以我只是为了做
chatTextLabel.text = chatMessage.message chatTextLabel?.updateConstraints()设置文本后

//完整的代码

 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) } 
 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ // call the method dynamiclabelHeightForText } 

使用上面的方法dynamic返回行的高度。 并将相同的dynamic高度分配给您使用的标签。

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

此代码可以帮助您find标签中文本显示的dynamic高度。

在我的情况下,单元格高度的问题发生在初始表格视图加载后,并发生用户操作(点击单元格中具有更改单元格高度效果的button)。 除非我这样做,否则我一直无法让细胞改变它的高度:

 [self.tableView reloadData]; 

我曾尝试过

 [cell layoutIfNeeded]; 

但是这并没有奏效。

在Swift 3中,每次更新可重用单元格的文本时都必须调用self.layoutIfNeeded()。

 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 

就我而言,我正在更新其他周期。 所以tableTextCell高度在labelText被设置后被更新了。 我删除了asynchronous块。

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

上述解决scheme都不起作用,但是下面的build议组合了。

必须在viewDidLoad()中添加以下内容。

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

reloadData,setNeedsLayout和layoutIfNeeded的上述组合工作,但没有任何其他。 可能是特定于项目中的单元格。 是的,不得不调用reloadData两次,使其工作。

在viewDidLoad中也设置如下

 tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = MyEstimatedHeight 

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

 cell.setNeedsLayout() cell.layoutIfNeeded() 

只要确保你没有在表视图的'willdisplaycell'委托方法中设置标签文本。 在“cellForRowAtindexPath”委托方法中设置标签文本进行dynamic高度计算。

别客气 :)

在我的情况下,单元格中的堆栈视图导致了问题。 这显然是一个错误。 一旦我删除它,问题就解决了。

我遇到了这个问题,并通过移动我的视图/标签初始化代码从tableView(willDisplay cell:) TO tableView(cellForRowAt:)

在我的情况下,我有一个自定义视图在我的单元格布局与自动布局,并在其重写updateConstraints约束激活/取消激活代码。

我不得不:

  • 为该视图设置高度约束,优先级为999
  • 在其中调用layoutIfNeeded覆盖didMoveToSuperview
  • layoutSubviews调用updateConstraints

我的自定义单元格中的代码

 override func didMoveToSuperview() { super.didMoveToSuperview() layoutIfNeeded() } override func layoutSubviews() { super.layoutSubviews() customView.updateConstraints() } func setUpUI() { [...] let heightConstraint = NSLayoutConstraint(item: customView, attribute: .height, relatedBy: .equal, toItem: contentView, attribute: .height, multiplier: 1, constant: 0) heightConstraint.priority = UILayoutPriority(999) contentView.addConstraint(heightConstraint) }