setNeedsLayout 仅在显示后重新布局单元格

Posted

技术标签:

【中文标题】setNeedsLayout 仅在显示后重新布局单元格【英文标题】:setNeedsLayout relayouts the cell only after is has been displayed 【发布时间】:2014-01-04 18:53:47 【问题描述】:

我有一个带有单元格的表格视图,它有时有一个可选的 UI 元素,有时它必须被删除。 根据元素调整标签大小。

当单元格初始化时,它会比稍后的要窄。当我将数据设置到标签中时,此代码从cellForRowAtIndexPath:调用

if (someFlag) 
    // This causes layout to be invalidated
    [flagIcon removeFromSuperview];
    [cell setNeedsLayout];

之后,单元格返回到表格视图,并显示出来。但是,此时的文本标签已调整其宽度,但未调整高度。大约一秒钟后调整高度,当所有单元格都已显示时,抖动清晰可见。

重要说明,这仅在最初创建前几个单元格期间。一旦它们被重用,一切都很好,因为可选视图被移除并且标签的大小已经与以前的用法一样正确。

为什么在setNeedsLayout 之后但在显示之前没有完全重新布局单元格? UIKit不应该在显示前检查无效布局吗?

如果我这样做

if (someFlag) 
    [flagIcon removeFromSuperview];
    [cell layoutIfNeeded];

所有内容都立即调整,但编写代码的方式似乎不正确,我觉得我错过了其他东西。


更多关于如何创建单元格的代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    ProfileCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    [cell setData:model.items[indexPath.row] forMyself:YES];
    return cell;


// And in ProfileCell:
- (void)setData:(Entity *)data forMyself:(BOOL)forMe

    self.entity = data;

    [self.problematicLabel setText:data.attributedBody];
    // Set data in other subviews as well

    if (forMe) 
        // This causes layouts to be invalidated, and problematicLabel should resize
        [self.reportButton removeFromSuperview];
        [self layoutIfNeeded];
    

另外,如果重要的话,在故事板单元格中看起来像这样,一旦删除标志图标,可选约束就会接管:

【问题讨论】:

您使用的是库存UITableViewCells 还是自定义单元格? 你能添加所有的 cellForRowAtIndexPath: 方法吗?如果您这样做,我想我将能够为您提供帮助:) 好的。这是我在使用UITableViews 时(痛苦地)学到的东西。 UITableViews 的 drawRect 方法不称为“总是”。我想您正在使用dequeueReusableCellWithCellIdentifier 重用单元格。您可以尝试做的一件事是在willDisplayCell 方法中放置一个[yourCell drawRect]。每当单元格即将移动到可见边界时,总是会调用此方法。此外,setNeedsLayoutsetNeedsDisplay 是对内存征税的方法,在具有大量内容的单元格中。 @Refael.S 我已经添加了代码,虽然代码不多,但我很确定。还添加了它在故事板中的外观。 @n00bProgrammer 谢谢,但是调用drawRectlayoutIfNeeded 更糟糕。此外,问题在于新单元格,而不是重复使用的单元格。 【参考方案1】:

我同意调用layoutIfNeeded 似乎是错误的,即使它适用于您的情况。但我怀疑你错过了什么。虽然我没有对这种方式进行任何研究,但根据我在经历动态布局的表格单元格中使用自动布局的经验, 有点错误。也就是说,在运行时向单元格中删除或添加子视图时,我看到了不稳定的布局。

如果您正在寻找替代策略(使用自动布局),您可以继承 UITableViewCell 并覆盖 layoutSubviews。自定义表格单元格可以在其公共 API 中公开一个标志,该标志可以在tableView:cellForRowAtIndexPath: 的实现中设置。单元格的layoutSubviews 方法将使用该标志来确定它是否应包含可选的 UI 元素。但是,我不保证这会消除问题。

第二种策略是设计两种不同的单元格类型,并根据需要在tableView:cellForRowAtIndexPath: 中的两者之间进行交换。

【讨论】:

我认为尝试影响布局比调用layoutIfNeeded 更加繁重和复杂。我曾考虑过两个单元格,但保持两个几乎完全相同的视图在未来更容易出错。 这里确实似乎没有其他事情要做,所以我会将您的答案标记为已接受。我没有找到任何其他方法可以解决此问题,也没有任何其他建议有效。【参考方案2】:

您已在问题中添加了额外的代码,所以我有另一个建议。在单元格的setData:forMyself: 方法中,尝试调用setNeedsUpdateConstraints 而不是layoutIfNeeded

【讨论】:

不会改变任何东西。实际上,当打印出needsUpdateConstraints 时,它已经是YES,布局也是如此,因为从superview 中删除已经使其无效。 所以,在原始代码中,[cell setNeedsLayout] 是多余的,我把它放在那里是为了避免回答“添加setNeedsLayout”。

以上是关于setNeedsLayout 仅在显示后重新布局单元格的主要内容,如果未能解决你的问题,请参考以下文章

setNeedsDisplay setNeedsLayout

iOS-setNeedsLayout等布局方法

setNeedsLayout 和 setNeedsDisplay

SDWebImage 下载后不显示(仅在重新打开 VController 后)

内容仅在重新加载后显示

仅在 Firefox 上重新加载后显示文本