Ios UITableView单元格折叠动画看起来不好

Ios UITableView单元格折叠动画看起来不好,ios,swift,uitableview,ios11,Ios,Swift,Uitableview,Ios11,我在使细胞塌陷动画看起来始终平滑时遇到问题 我的形象胜过千言万语。以下是相同表视图的两个GIF: 折叠对单元格“6”非常有效,但对单元格“5”无效。看起来整个UITableView内容在执行折叠动画之前都会跳起来 我不是在使用UITableView的行高的UITableView自动维度。相反,我通过tableView(\utableview:UITableView,heightForRowAt indexath:indexPath)方法提供行高,并使用tableView的beginUpdat

我在使细胞塌陷动画看起来始终平滑时遇到问题

我的形象胜过千言万语。以下是相同表视图的两个GIF:

折叠对单元格“6”非常有效,但对单元格“5”无效。看起来整个UITableView内容在执行折叠动画之前都会跳起来

我不是在使用UITableView的
行高的
UITableView自动维度。相反,我通过
tableView(\utableview:UITableView,heightForRowAt indexath:indexPath)
方法提供行高,并使用tableView的
beginUpdates
/
endUpdates
展开/折叠单元格。如您所见,我还使用
tableView.scrollToRow(at:indepath,at:.none,animated:true)滚动到展开的行
(但仅限于展开时,所以这可能无关紧要)

UITableViewController的子类嵌入在带有大标题和搜索栏的UINavigationController中(此处不可见,因为内容向下滚动)。UINavigationController嵌入在UITabBarController中

下面是相关的展开/折叠部分。当传递nil
indepath
参数时,已经扩展的单元格将崩溃

func expandRow(at indexPath: IndexPath?) {
    selectedIndexPath = indexPath
    tableView.beginUpdates()
    tableView.endUpdates()

    if let ip = indexPath {
        tableView.scrollToRow(at: ip, at: .none, animated: true)
    }
}
此方法计算行高,并由
tableView(\u0:heightForRowAt:)
tableView(\u0:estimatedHeightForRowAt:)
调用:

以下是GitHub回购协议:

你知道如何改进吗

此问题是尺寸敏感问题(在屏幕大小不同的设备上执行相同步骤后,此问题可能无法再现)。这是用iOS 11.3在iPhone7模拟器上录制的


编辑1:
保留自动滚动到展开单元格的功能非常重要。

您正在滚动到展开的行,这使得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
beginUpdate()
endUpdates()
方法。更新的代码示例

我能够通过对展开方法的细微更改使其变得平滑:

func expandRow(at indexPath: IndexPath?) {
    selectedIndexPath = indexPath
    tableView.beginUpdates()
    tableView.endUpdates()

    if let ip = indexPath {
        tableView.scrollToRow(at: ip, at: .middle, animated: true)
    }
}

我有个主意。我不确定诀窍是否像我想的那样有效

可折叠表格的一项功能是,当表格视图展开或折叠时,其高度将发生变化

如果使用从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
    }
}

在为每行单击调整我的行高时出现此问题。与@Taras的答案几乎相同,尽管在这里,在分配保存的contentOffset之前,需要先删除动画。希望这有帮助:)


你认为这两行带有
.beginUpdate()
.endUpdates()
的代码在做什么?据我所知,这是一种众所周知的技术,可以使表格视图以动画方式更新单元格高度。单元格高度的计算方法如下:(我粘贴了原始问题,因为注释中没有代码格式)@AleksanderMaj您找到解决方案了吗?@guru不幸的是,没有。我很久以前就放弃了此解决方案。此解决方案将导致大多数顶部和底部单元格意外跳跃感谢您的回答,但正如@TarasChernyshenko指出的,它引入了其他问题。感谢您的回复。滚动部分仅在展开时执行(即
indepath!=nil
),它对折叠单元格没有影响。这不是问题的一部分。您的响应中的contentOffset部分根本不起作用。展开单元格“5”,然后手动滚动,使其底边与选项卡栏顶部对齐。然后再次点击“5”将其折叠。它仍然在跳跃:
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
    }
}
func expandRow() {
    let savedContentOffset = tableView.contentOffset

    tableView.beginUpdates()
    tableView.endUpdates()
    tableView.layer.removeAllAnimations()

    tableView.setContentOffset(savedContentOffset, animated: false)
}