iOS8 更改动态单元格的高度 "now",重新调整内部单元格内容
iOS8 change height of dynamic cell "now", re internal cell content
这是一个动态单元格
注意 - 在示例中,文本不是数据驱动的。它只是单元格本地的一些文本(例如,考虑帮助文本)。在运行时,使用实际位于单元格内的按钮将 UILabel 的 .text 从一个词更改为多行。 iOS 完全调整单元格大小并 table...
...但 仅当单元格滚动到屏幕外,然后再次滚动时。
如何提醒 table 视图重新计算所有内容 "now"?
(请注意,此问题仅适用于 iOS8+、Xcode7+、动态单元格高度的自动布局。)
您是否尝试在单元格索引上调用 reloadRowsAtIndexPaths?如果约束设置正确,它应该动画到新大小。
我不知道你的代码,但你真的在主线程上执行了你的 ui 更改。同样的问题发生在我身上,通过将 execuion 放在主线程上解决了。
dispatch_async(dispatch_get_main_queue()) { () -> Void in
[...]
}
您应该在 更改单元格标签的文本后调用 self.tableView.reloadData()
。
它将强制 tableView
重绘单元格。这就是滚动时发生的情况,单元格正在 reused
,并在再次返回时重新绘制。
编辑:
如果您不能或不愿在 tableView 上执行 reloadData,您可以使用:
self.tableView.beginUpdates()
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(row:0 section:0)] withRowAnimation:UITableViewRowAnimation.Automatic)
self.tableView.endUpdates()
改变高度
所以基本上,有两种方法可以做到:
第一个是实际重新加载单元格(不是 table视图)。重新加载将调用新的 heightForRow(不要忘记清除缓存,如果您正在缓存尺寸),这将 return 适当的新高度:
let indexPaths = [NSIndexPath(forRow: ~the rows in question~, inSection: 0)]
self.table.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
(但请注意,这通常涉及重新加载不止一行;特别是如果您 select/deselect,则必须重新加载所有已更改的行。)
但是,如果您只想更改单元格的大小和内容本身,而不是真正更改数据内容...例如:
您单击了某个按钮并将单元格中的新本地文本分配给出口(可能是帮助文本):
您只更改了单元格的布局。例如,您将字体变大,或更改了文本块的边距,从而改变了文本块的高度,因此整个单元格的高度确实发生了变化:
在这种情况下,无需重新加载,只需调用以下命令,这会强制 table 视图基本上执行所有动画,为此它需要新的高度,因此请求它:
self.table.beginUpdates()
self.table.endUpdates()
正解
我明白你的问题是什么了。您正在尝试从实际单元格更改单元格的高度 - 但您不会成功 -> 而且您不应该这样做。看,单元格是视图,视图不应该对其数据有任何想法 - 视图就是呈现的内容。如果您需要任何更改,您应该通知您的控制者这样做。为此,您可以使用通知,但最好使用协议/委托。
所以首先你在你的单元格中创建协议,它将用于通知控制器,有一个变化:
protocol MyCellDelegate {
func buttonTappedForCell(cell : UITableViewCell)
}
现在,您需要在包含 table:
的视图控制器中遵守该协议
class MyClassWithTableView : MyCellDelegate
最后,您需要在单元格中声明委托:
class MyCell {
var delegate : MyCellDelegate
}
并在您可能在视图控制器中拥有的单元配置中分配它:
cell.delegate = self
这是所有委托/协议的基本设置,现在,当您单击按钮时,您可以将操作转发给您的控制器:
@IBAction myButtonTouchUpInside() {
self.delegate.buttonTappedForCell(self)
}
完成所有这些后,按照第 1 部分进行操作。也就是说,reloadRowsAtIndexPaths
或 beginUpdates
/ endUpdates
对,如上所述。
希望对您有所帮助!
我假设您不是在 cellForRowAtIndexPath
内设置 UILabel
的 text
属性,而是在其他地方设置(或异步设置)。如果是这样,我不会在那里更新 UI。相反,我会更新支持 table 的模型,然后调用 reloadRowsAtIndexPaths
。这将使 cellForRowAtIndexPath
再次调用,但与重新加载整个 table 不同,这将优雅地保持 table 视图的 contentOffset
就在原处。
我知道这听起来过于复杂,但归根结底,您不拥有此视图,table 视图拥有。它必须做各种超越更新单元格的事情。即,如果单元格变大,找出哪些单元格滚出视图并将它们出列。如果单元格缩小,则找出哪些单元格因此滚动到视图中。
这是一种非常复杂的舞蹈。您可以尝试在单元格上调用 setNeedsLayout
,但我不希望它起作用(即使它起作用,也是一种脆弱的方法)。 table 视图负责管理它的单元格,所以如果你真的应该更新模型并重新加载那个单元格。
这是一个动态单元格
注意 - 在示例中,文本不是数据驱动的。它只是单元格本地的一些文本(例如,考虑帮助文本)。在运行时,使用实际位于单元格内的按钮将 UILabel 的 .text 从一个词更改为多行。 iOS 完全调整单元格大小并 table...
...但 仅当单元格滚动到屏幕外,然后再次滚动时。
如何提醒 table 视图重新计算所有内容 "now"?
(请注意,此问题仅适用于 iOS8+、Xcode7+、动态单元格高度的自动布局。)
您是否尝试在单元格索引上调用 reloadRowsAtIndexPaths?如果约束设置正确,它应该动画到新大小。
我不知道你的代码,但你真的在主线程上执行了你的 ui 更改。同样的问题发生在我身上,通过将 execuion 放在主线程上解决了。
dispatch_async(dispatch_get_main_queue()) { () -> Void in
[...]
}
您应该在 更改单元格标签的文本后调用 self.tableView.reloadData()
。
它将强制 tableView
重绘单元格。这就是滚动时发生的情况,单元格正在 reused
,并在再次返回时重新绘制。
编辑:
如果您不能或不愿在 tableView 上执行 reloadData,您可以使用:
self.tableView.beginUpdates()
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(row:0 section:0)] withRowAnimation:UITableViewRowAnimation.Automatic)
self.tableView.endUpdates()
改变高度
所以基本上,有两种方法可以做到:
第一个是实际重新加载单元格(不是 table视图)。重新加载将调用新的 heightForRow(不要忘记清除缓存,如果您正在缓存尺寸),这将 return 适当的新高度:
let indexPaths = [NSIndexPath(forRow: ~the rows in question~, inSection: 0)]
self.table.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
(但请注意,这通常涉及重新加载不止一行;特别是如果您 select/deselect,则必须重新加载所有已更改的行。)
但是,如果您只想更改单元格的大小和内容本身,而不是真正更改数据内容...例如:
您单击了某个按钮并将单元格中的新本地文本分配给出口(可能是帮助文本):
您只更改了单元格的布局。例如,您将字体变大,或更改了文本块的边距,从而改变了文本块的高度,因此整个单元格的高度确实发生了变化:
在这种情况下,无需重新加载,只需调用以下命令,这会强制 table 视图基本上执行所有动画,为此它需要新的高度,因此请求它:
self.table.beginUpdates()
self.table.endUpdates()
正解
我明白你的问题是什么了。您正在尝试从实际单元格更改单元格的高度 - 但您不会成功 -> 而且您不应该这样做。看,单元格是视图,视图不应该对其数据有任何想法 - 视图就是呈现的内容。如果您需要任何更改,您应该通知您的控制者这样做。为此,您可以使用通知,但最好使用协议/委托。
所以首先你在你的单元格中创建协议,它将用于通知控制器,有一个变化:
protocol MyCellDelegate {
func buttonTappedForCell(cell : UITableViewCell)
}
现在,您需要在包含 table:
的视图控制器中遵守该协议 class MyClassWithTableView : MyCellDelegate
最后,您需要在单元格中声明委托:
class MyCell {
var delegate : MyCellDelegate
}
并在您可能在视图控制器中拥有的单元配置中分配它:
cell.delegate = self
这是所有委托/协议的基本设置,现在,当您单击按钮时,您可以将操作转发给您的控制器:
@IBAction myButtonTouchUpInside() {
self.delegate.buttonTappedForCell(self)
}
完成所有这些后,按照第 1 部分进行操作。也就是说,reloadRowsAtIndexPaths
或 beginUpdates
/ endUpdates
对,如上所述。
希望对您有所帮助!
我假设您不是在 cellForRowAtIndexPath
内设置 UILabel
的 text
属性,而是在其他地方设置(或异步设置)。如果是这样,我不会在那里更新 UI。相反,我会更新支持 table 的模型,然后调用 reloadRowsAtIndexPaths
。这将使 cellForRowAtIndexPath
再次调用,但与重新加载整个 table 不同,这将优雅地保持 table 视图的 contentOffset
就在原处。
我知道这听起来过于复杂,但归根结底,您不拥有此视图,table 视图拥有。它必须做各种超越更新单元格的事情。即,如果单元格变大,找出哪些单元格滚出视图并将它们出列。如果单元格缩小,则找出哪些单元格因此滚动到视图中。
这是一种非常复杂的舞蹈。您可以尝试在单元格上调用 setNeedsLayout
,但我不希望它起作用(即使它起作用,也是一种脆弱的方法)。 table 视图负责管理它的单元格,所以如果你真的应该更新模型并重新加载那个单元格。