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

bla*_*ckp 107 uitableview ios autolayout ios8 ios-autolayout

我有一个使用自动布局在故事板中定义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;
}
Run Code Online (Sandbox Code Playgroud)

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

我已经创建了一个演示此行为的示例项目.

示例项目:首次加载时示例项目的表格视图顶部. 示例项目:向下滚动和备份后的相同单元格.

rin*_*aro 128

我不知道这是否有明确记录,但[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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我确实很奇怪,为什么第一次通过它只是必要的?如果我在单元格的`-awakeFromNib`中调用`-layoutIfNeeded`,它也可以正常工作.我更喜欢只调用`layoutIfNeeded`,我知道为什么这是必要的. (2认同)
  • 为我工作!我很想知道为什么需要它! (2认同)

Mic*_*son 35

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

override func didMoveToSuperview() {
    super.didMoveToSuperview()
    layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个真正的宝石.谢谢. (4认同)
  • 别忘了调用`super.didMoveToSuperview()` (3认同)
  • @IvanSmetanin不管默认实现不做任何事情都没有关系,您仍然应该调用super方法,因为它将来可能会做一些事情。 (3认同)

小智 21

添加[cell layoutIfNeeded]cellForRowAtIndexPath不为最初滚出的视图细胞起作用.

它也没有表面[cell setNeedsLayout].

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

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

  • 取消选中"使用大小类"对我们有用! (3认同)
  • 使用`[cell layoutSubviews]`而不是layoutIfNeeded可能是可能的修复.请参阅http://stackoverflow.com/a/33515872/1474113 (3认同)

Vit*_*nko 13

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

为什么会这样?

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

这是一个问题:

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

因此它计算了标签和单元格的高度,宽度为400px.但是你在带有屏幕的设备上运行,例如320px.并且这个自动计算的高度不正确.只是因为细胞layoutSubviews只在发生后才发生tableView:heightForRowAtIndexPath: 即使preferredMaxLayoutWidth手动设置标签layoutSubviews也无济于事.

我的解决方案

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;
}
Run Code Online (Sandbox Code Playgroud)

2)子类UITableViewCell.preferredMaxLayoutWidth手动为您的标签设置layoutSubviews.你还需要手动布局contentView,因为它不会在单元格框架更改后自动布局(我不知道为什么,但它是)

- (void)layoutSubviews {
    [super layoutSubviews];
    [self.contentView layoutIfNeeded];
    self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width;
}
Run Code Online (Sandbox Code Playgroud)


alv*_*oun 10

我有一个类似的问题,在第一次加载时,没有计算行高,但在一些滚动或转到另一个屏幕后,我回到这个屏幕行计算.在第一次加载时,我的项目从互联网加载,在第二次加载时,我的项目首先从Core Data加载并从互联网上重新加载,我注意到行高度是在从互联网重新加载时计算的.所以我注意到当在segue动画期间调用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()
    })
Run Code Online (Sandbox Code Playgroud)

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


JAH*_*lia 9

上述解决方案都不适合我,有效的是这个魔法秘诀:按以下顺序调用它们:

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

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


Cla*_*aus 5

在我的情况下,第一次显示单元格时,UILabel的最后一行被截断。它是随机发生的,正确调整大小的唯一方法是将单元格滚动出视图,然后将其放回原处。我尝试了到目前为止显示的所有可能的解决方案(layoutIfNeeded..reloadData),但对我没有任何帮助。诀窍是一套“自动收缩”Minimuum字体比例(0.5给我)。试试看

  • 但这会自动收缩文本...为什么要在表格视图中使用不同大小的文本?看起来糟透了。 (2认同)

bik*_*ota 5

为表格视图自定义单元格中的所有内容添加约束,然后估计表格视图行高并将行高设置为在 viewdid 加载中的自动尺寸:

    override func viewDidLoad() {
    super.viewDidLoad()

    tableView.estimatedRowHeight = 70
    tableView.rowHeight = UITableViewAutomaticDimension
}
Run Code Online (Sandbox Code Playgroud)

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

class CustomTableViewCell: UITableViewCell {

override func awakeFromNib() {
    super.awakeFromNib()
    self.layoutIfNeeded()
    // Initialization code
}
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*Vig 5

我已经尝试了该问题的大部分答案,但无法解决任何问题。我发现的唯一功能解决方案是将以下内容添加到我的UITableViewController子类中:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    UIView.performWithoutAnimation {
        tableView.beginUpdates()
        tableView.endUpdates()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 黑魔法。在`viewWillAppear` 中这样做对我不起作用,但在`viewDidAppear` 中这样做。 (4认同)