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];
另外,如果重要的话,在故事板单元格中看起来像这样,一旦删除标志图标,可选约束就会接管:
【问题讨论】:
您使用的是库存UITableViewCell
s 还是自定义单元格?
你能添加所有的 cellForRowAtIndexPath: 方法吗?如果您这样做,我想我将能够为您提供帮助:)
好的。这是我在使用UITableViews
时(痛苦地)学到的东西。 UITableView
s 的 drawRect
方法不称为“总是”。我想您正在使用dequeueReusableCellWithCellIdentifier
重用单元格。您可以尝试做的一件事是在willDisplayCell
方法中放置一个[yourCell drawRect]
。每当单元格即将移动到可见边界时,总是会调用此方法。此外,setNeedsLayout
和 setNeedsDisplay
是对内存征税的方法,在具有大量内容的单元格中。
@Refael.S 我已经添加了代码,虽然代码不多,但我很确定。还添加了它在故事板中的外观。
@n00bProgrammer 谢谢,但是调用drawRect
比layoutIfNeeded
更糟糕。此外,问题在于新单元格,而不是重复使用的单元格。
【参考方案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
setNeedsLayout 和 setNeedsDisplay