iOS8“现在”改变动态单元格的高度,重新内部单元格内容
Posted
技术标签:
【中文标题】iOS8“现在”改变动态单元格的高度,重新内部单元格内容【英文标题】:iOS8 change height of dynamic cell "now", re internal cell content 【发布时间】:2015-09-19 21:48:10 【问题描述】:这是一个动态单元格
注意 - 在示例中,文本不是数据驱动的。它只是单元格本地的一些文本(例如,考虑帮助文本)。在运行时,使用单元格内的按钮将 UILabel 的 .text 从一个单词更改为多行。 ios 完美调整单元格和表格的大小....
...但仅当单元格滚动到屏幕外,然后再次打开时。
如何提醒表格视图“现在”重新计算所有内容?
(请注意,此问题仅适用于 iOS8+、Xcode7+、动态单元格高度的自动布局。)
【问题讨论】:
emm,我想你应该在为标签分配新文本时以某种方式触发自动布局,并且内部魔法确实休息了。还是有什么不那么直截了当的? 我添加了解释为什么您不应该 - 永远 - 更改单元格中的内容,并添加了有关如何执行此操作的解决方案。 【参考方案1】:改变高度
所以基本上,有两种方法可以做到:
第一个是实际重新加载单元格(不是表格视图)。重新加载将调用新的 heightForRow(如果您正在缓存大小,请不要忘记清除缓存),这将返回正确的新高度:
let indexPaths = [NSIndexPath(forRow: ~the rows in question~, inSection: 0)]
self.table.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
(但是请注意,这通常涉及重新加载多行;特别是如果您选择/取消选择,则必须重新加载所有更改的行。)
如果您只想更改单元格的大小和内容本身,并没有真正更改数据内容......例如:
您单击了某个按钮并将单元格中的新本地文本分配给了插座(可能是帮助文本):
您只更改了单元格的布局。例如,您将字体变大,或者更改了文本块的边距,从而使文本块的高度发生了变化,因此整个单元格的高度确实发生了变化:
在这种情况下,而不是重新加载,只需调用以下命令,这会强制 tableview 基本上执行所有动画,为此它需要新的高度,所以它请求它:
self.table.beginUpdates()
self.table.endUpdates()
真正的解决方案
我明白你的问题是什么。您正在尝试从实际单元格更改单元格的高度 - 但您不会成功 -> 而且您不应该这样做。看,单元格是视图,视图不应该对它的数据有任何想法——视图就是呈现的东西。如果您需要任何更改,您应该通知您的控制器这样做。为此,您可以使用通知,但最好使用协议/委托。
因此,首先您在单元格中创建协议,该协议将用于通知控制器存在更改:
protocol MyCellDelegate
func buttonTappedForCell(cell : UITableViewCell)
现在,您需要在包含表的视图控制器中遵守该协议:
class MyClassWithTableView : MyCellDelegate
最后,你需要在单元格中声明委托:
class MyCell
var delegate : MyCellDelegate
并在单元格的配置中分配它,您可能在视图控制器中拥有它:
cell.delegate = self
这是所有委托/协议的基本设置,现在,当您单击按钮时,您可以将操作转发给您的控制器:
@IBAction myButtonTouchUpInside()
self.delegate.buttonTappedForCell(self)
完成所有这些后,按照第 1 部分进行操作。也就是说,reloadRowsAtIndexPaths
或 beginUpdates
/ endUpdates
对,如上所述。
希望对你有帮助!
【讨论】:
我为你的其他问题添加了解决方案,因为我终于明白了你真正想要做什么:) 嗨,吉里!我意识到您对reloadRowsAtIndexPaths
与beginUpdates
/ endUpdates
对之间的区别的解释是至关重要的。实际上,我继续大胆地编辑了您的答案以扩展这一点。看看你喜不喜欢....
顺便说一句,我刚刚发现 setNeedsUpdateConstraints
。 . .有趣的是,没有人在这种情况下提到它。它的作用类似于'reloadRowsAtIndexPaths
这就是为什么它不应该这样 :) ***.com/questions/20609206/…
Jiri,感谢您提供大量有用的信息,我相信这些信息对未来的谷歌用户也将非常有用。我点击了一个象征性的赏金以表示感谢,谢谢!【参考方案2】:
我假设您没有在cellForRowAtIndexPath
内设置UILabel
的text
属性,而是在其他地方设置(或异步执行)。如果是这种情况,我不会在那里更新 UI。相反,我会更新支持表格的模型,然后调用reloadRowsAtIndexPaths
。这将使cellForRowAtIndexPath
再次调用,但与重新加载整个表格不同,这将优雅地保持表格视图的contentOffset
正确。
我知道这一切听起来都不必要地复杂,但底线是您不拥有该视图,而表格视图拥有。除了更新单元格之外,它还必须做各种事情。即,如果单元格增长,找出哪些单元格滚动出视图并将它们出列。如果单元格缩小,请找出哪些单元格因此滚动到视图中。
这是一种非常复杂的舞蹈。您可以尝试在单元格上调用setNeedsLayout
,但我不希望它起作用(即使它起作用,它也是一种脆弱的方法)。表格视图负责管理其单元格,因此如果您真的应该更新模型并重新加载该单元格。
【讨论】:
明白了,这很有意义。你只是不能让细胞对自己做任何事情,真的。毕竟它是一个实体,可能在屏幕上也可能不在屏幕上,可以从原型中随时形成或重新形成等等。(重新设置NeedsLayout,它似乎什么都不做!) 全面披露,有时人们会直接异步更新单元格。 (最常见的示例包括所有用于异步图像加载的各种UIImageView
类别。)但这些仅在单元格高度不变的情况下才有效。在架构上,这是错误的方法。最好更新模型,然后调用reloadRowsAtIndexPaths
。【参考方案3】:
您是否尝试在单元格索引上调用 reloadRowsAtIndexPaths?如果约束设置正确,它应该动画到新的大小。
【讨论】:
【参考方案4】:您应该在更改单元格标签的文本之后致电self.tableView.reloadData()
。
它将强制tableView
重绘单元格。这就是你滚动时发生的情况,单元格是reused
,当它再次返回时会重新绘制。
编辑:
如果您不能或不会在您的 tableView 上执行 reloadData,您可以使用:
self.tableView.beginUpdates()
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(row:0 section:0)] withRowAnimation:UITableViewRowAnimation.Automatic)
self.tableView.endUpdates()
【讨论】:
其实如果你的内容是静态的并且是那种“帮助”的形式,一个简单的 reloadData() 会很快的 @JoeBlow 一点也不,reloadRowsAtIndexPaths 只会重新加载您提供的行数组。如果您提供一个只有一行的数组,它将只重新加载这一行。没有其他方法可以提高效率 顺便说一句,beingUpdates
和 endUpdates
仅在您要一起执行多个更新时才需要。如果是单个reloadRowsAtIndexPaths
,则不需要begin
/endUpdates
。
但是@Rob我意识到,如果我没记错的话,如果你只是改变了布局,你确实可以调用beginUpdates
/ endUpdates
对(假设将点大小增加三倍)文本中的字体,因此单元格现在要长得多)。相反,如果(正如我的问题所问的那样)您实际上是在更改模型数据(我们现在知道,应该),那么确实必须重新加载。我认为听起来不错........(我希望:))
@JoeBlow - 是的,正如文档所说“您还可以使用 [beginUpdates
] 后跟 endUpdates
方法来动画行高的变化,而无需重新加载单元格。”但是调用beginUpdates
,然后重新加载一个单元格,然后调用endUpdates
是多余的。【参考方案5】:
我不知道你的代码,但你真的在主线程上执行了你的 ui 更改吗?同样的问题也发生在我身上,通过将执行放在主线程上解决了。
dispatch_async(dispatch_get_main_queue()) () -> Void in
[...]
【讨论】:
嗨 Philipp,这是一个很好的观点,但恐怕不是我的特殊问题!以上是关于iOS8“现在”改变动态单元格的高度,重新内部单元格内容的主要内容,如果未能解决你的问题,请参考以下文章