UITableView 单元格折叠动画看起来很糟糕
Posted
技术标签:
【中文标题】UITableView 单元格折叠动画看起来很糟糕【英文标题】:UITableView cell collapse animation looks bad 【发布时间】:2018-05-23 10:31:54 【问题描述】:我在使单元格折叠动画看起来始终流畅时遇到问题。
我的形象值一千字。这是同一个表格视图的两个 GIF:
折叠对单元格“6”非常有效,但不适用于“5”。看起来整个 UITableView 内容在执行折叠动画之前都跳了起来。
我不将UITableViewAutomaticDimension
用于UITableView 的rowHeight
。相反,我通过tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
方法提供行高,并使用表格视图的beginUpdates
/endUpdates
展开/折叠单元格。如您所见,我还使用tableView.scrollToRow(at: indexPath, at: .none, animated: true)
滚动到展开的行(但仅在展开时,因此可能无关紧要)。
UITableViewController 的子类嵌入在带有大标题和搜索栏的 UINavigationController 中(此处不可见,因为内容向下滚动)。 UINavigationController 嵌入在 UITabBarController 中。
这是相关的展开/折叠部分。当传递一个 nil indexPath
参数时,已经展开的单元格将折叠。
func expandRow(at indexPath: IndexPath?)
selectedIndexPath = indexPath
tableView.beginUpdates()
tableView.endUpdates()
if let ip = indexPath
tableView.scrollToRow(at: ip, at: .none, animated: true)
这个方法计算行高,tableView(_:heightForRowAt:)
和tableView(_:estimatedHeightForRowAt:)
都调用:
private func heightForRow(at indexPath: IndexPath) -> CGFloat
var height: CGFloat
if selectedIndexPath == indexPath
// Add some height variety
height = 300 + CGFloat((indexPath.row % 4) * 20)
else
height = 70
return height
这里是 GitHub 代码库:https://github.com/AleksanderMaj/CellCollapse
知道如何改进吗?
此问题与尺寸有关(在具有不同屏幕尺寸的设备上执行相同步骤后,此问题可能无法重现)。这是在装有 ios 11.3 的 iPhone 7 模拟器上录制的。
编辑 1: 保持自动滚动到展开的单元格很重要。
【问题讨论】:
你认为.beginUpdates()
和.endUpdates()
这两条线在做什么?
据我了解,这是一种众所周知的技术,它使表格视图以动画方式更新单元格高度。单元格高度的计算方式如下:(我粘贴在原始问题中,因为cmets中没有代码格式)
@AleksanderMaj 你找到解决办法了吗?
@guru 不幸的是没有。我很久以前就放弃了
【参考方案1】:
通过对您的扩展方法进行微小更改,我能够使操作变得顺利:
func expandRow(at indexPath: IndexPath?)
selectedIndexPath = indexPath
tableView.beginUpdates()
tableView.endUpdates()
if let ip = indexPath
tableView.scrollToRow(at: ip, at: .middle, animated: true)
【讨论】:
这个解决方案会导致大多数顶部和底部单元格的意外跳转monosnap.com/file/TBg6Wl9iZRoH9SUqVmaLSFYUO6V9zc 感谢您的回答,但正如@TarasChernyshenko 指出的那样,它引入了其他问题。【参考方案2】:在为每行点击调整行高时遇到此问题。与@Taras 的答案几乎相同,虽然在这里,需要先删除动画,然后再分配保存的 contentOffset。希望这会有所帮助:)
func expandRow()
let savedContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(savedContentOffset, animated: false)
【讨论】:
【参考方案3】:您正在滚动到展开的行,这使得 UITableView 有点跳跃。
所以这里有一个重构的expandRow(at:)
方法:
func expandRow(at indexPath: IndexPath?)
self.contentOffset = self.tableView.contentOffset
selectedIndexPath = indexPath
tableView.beginUpdates()
tableView.endUpdates()
if let contentOffset = self.contentOffset
tableView.contentOffset = contentOffset
您还需要将var contentOffset: CGPoint?
属性添加到您的控制器。
更新
我错了,你不需要把selectedIndexPath = indexPath
btw beginUpdates()
和endUpdates()
方法。更新代码示例
【讨论】:
感谢您的回复。滚动部分仅在展开时(即indexPath != nil
时)执行,对折叠单元格没有影响。这不是问题的一部分。您的响应的 contentOffset 部分根本不起作用。展开单元格“5”,然后手动滚动,使其底部边缘与选项卡栏的顶部对齐。然后再次点击“5”以折叠它。它还在跳:youtu.be/kvyjKrWnlnM【参考方案4】:
我有个主意。我不确定这个技巧是否像我想象的那样奏效。
Collapsible table 有一个特性,就是在展开或折叠时,table view 的高度会发生变化。
如果我们使用继承自 UITableView 的自定义视图,我们可以覆盖视图的属性 contentSize 和 contentOffset。 通过覆盖这两个属性,我们可以让表格视图停止,禁用滚动,一段时间。
var contentSizeChanging: Bool = false
override var contentSize: CGSize
willSet
contentSizeChanging = true
didSet
contentSizeChanging = false
override var contentOffset: CGPoint
get
return super.contentOffset
set(newValue)
if contentSizeChanging return
super.contentOffset = newValue
【讨论】:
以上是关于UITableView 单元格折叠动画看起来很糟糕的主要内容,如果未能解决你的问题,请参考以下文章
当上边距不完全可见时,UITableView 单元格展开动画会发生变化