UITableView动态单元格高度仅在某些滚动后才能正确显示

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UITableView动态单元格高度仅在某些滚动后才能正确显示相关的知识,希望对你有一定的参考价值。

我有一个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;
}

有什么我可能会遗漏,导致自动布局在前几次无法完成其工作吗?

我创建了一个sample project来演示这种行为。

答案

我不知道这是否有明确记录,但在返回单元格之前添加[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;
}
另一答案

Attatching screenshot for your referance对我来说,这些方法都没有奏效,但我发现标签在Interface Builder中设置了明确的Preferred Width。删除它(取消选中“Explicit”),然后使用UITableViewAutomaticDimension按预期工作。

另一答案

cell.layoutIfNeeded()中调用cellForRowAtios 10和ios 11上为我工作,但不在ios 9上。

为了在ios 9上完成这项工作,我打电话给cell.layoutSubviews(),它就是诀窍。

另一答案

我尝试了此页面中的所有解决方案,但取消选中使用大小类,然后再次检查它解决了我的问题。

编辑:取消选中大小类会导致故事板上出现很多问题,所以我尝试了另一种解决方案。我在视图控制器的viewDidLoadviewWillAppear方法中填充了我的表视图。这解决了我的问题。

另一答案

我有调整标签大小的问题,所以我只是想做 设置文本后,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)

  }
另一答案

就我而言,我正在更新其他周期。因此在设置labelText后更新了tableViewCell高度。我删除了异步块。

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
     // }
}
另一答案

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

别客气 :)

另一答案
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{


//  call the method dynamiclabelHeightForText
}

使用上面的方法动态返回行的高度。并为您正在使用的标签指定相同的动态高度。

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


}

此代码可帮助您查找标签中文本显示的动态高度。

另一答案

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

[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
另一答案

以上解决方案均无效,但以下建议的组合确实有效。

不得不在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() 
另一答案

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

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

这似乎是一个真正的错误,因为我非常熟悉AutoLayout以及如何使用UITableViewAutomaticDimension,但是我偶尔会遇到这个问题。我很高兴终于找到了可以解决的问题。

另一答案

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

另一答案

我遇到了这个问题并通过将我的视图/标签初始化代码FROM tableView(willDisplay cell:)移动到tableView(cellForRowAt:)来修复它。

另一答案

问题是在我们有一个有效的行高之前,初始单元格会加载。解决方法是在视图出现时强制重新加载表。

- (void)viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:animated];
  [self.tableView reloadData];
}
另一答案

以上解决方案都没有为我工作,有效的是这个神奇的方法:按此顺序调用它们:

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

我的tableView数据是从Web服务填充的,在我写上面行的连接的回调中。

另一答案

对于仍然面临此问题的人,请尝试这条神奇的线条

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

只需确保在填充表格之后调用此数据,这样您就可以确定第0部分和第0行始终可用,否则它会崩溃,您可以先检查它们的可用性,希望这会有所帮助。

另一答案

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

它也没有与[cell setNeedsLayout]前面。

您仍然需要滚动某些单元格并返回视图,以便它们正确调整大小。

这非常令人沮丧,因为大多数开发人员都有动态类型,自动布局和自我调整单元正常工作 - 除了这个烦人的情况。这个bug影响了我所有的“更高”的表视图控制器。

另一答案

我在我的一个项目中有相同的经验。

为什么会这样?

在故事板中设计的单元格,某些设备的宽度。例如400px。例如,您的标签具有相同的宽度。从故事板加载时,它的宽度为400px。

这是一个问题:

以上是关于UITableView动态单元格高度仅在某些滚动后才能正确显示的主要内容,如果未能解决你的问题,请参考以下文章

如何让 UITableView 在动态高度单元格中滚动到底部?

具有动态单元格高度的 UITableView 的 reloadData() 导致跳跃滚动

关闭另一个视图后 UITableView 滚动内容大小不正确

UITableview 单元格高度在 iOS 14.0 中的滚动表视图上发生变化

根据图像问题的 UITableview 单元格高度

当单元格高度为动态时设置 UITableView 的高度