使用UITableViewAutomaticDimension时,UITableView在begin/endUpdates之后跳转

Sak*_*boy 24 uitableview ios

问题:当我在底部的的UITableView(且仅当在底部)和tableView.beginUpdates()/ tableView.endUpdates()被调用时,UITableView跳了一点点.我不希望它这样做.

设置:我有一个UITableViewUITableViewCells将全部在不同的大小和我使用UITableViewAutomaticDimension大小的细胞.

示例: 当我在底部时UITableView,我选择一个单元格,随后调用tableView.beginUpdates()/ tableView.endUpdates(),UITableView滚动/跳起一点点.

有没有人找到一种方法来做到这一点,所以没有跳跃后呼叫tableView.beginUpdates()/ tableView.endUpdates()在结束时UITableView

暂时我正在UITableViewCells手动计算我的全部,但我宁愿不这样做,因为我想利用它UITableViewAutomaticDimension.

代码:

这里有一些代码来显示tableView.beginUpdates()/ 之间发生的事情tableView.endUpdates():

    tableView.beginUpdates()
    cell!.setNeedsUpdateConstraints()
    cell!.updateConstraintsIfNeeded()
    cell!.setNeedsLayout()
    cell!.layoutIfNeeded()
    tableView.endUpdates()
Run Code Online (Sandbox Code Playgroud)

我的观点会发现:

public override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    self.tableView.estimatedRowHeight = 150.0
    self.tableView.rowHeight = UITableViewAutomaticDimension


    self.tableView.reloadData()
    //Bug in 8.0+ forces us to call three extra methods to get dynamic cell heights to work correctly...
    self.tableView.setNeedsLayout()
    self.tableView.layoutIfNeeded()
    self.tableView.reloadData()
}
Run Code Online (Sandbox Code Playgroud)

我的身高:

public override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    return UITableViewAutomaticDimension

}
Run Code Online (Sandbox Code Playgroud)

更新:删除cell!.setNeedsUpdateConstraints(),cell!.updateConstraintsIfNeeded(),cell!.setNeedsLayout(),和cell!.layoutIfNeeded(从)tableView.beginUpdates()/ tableView.endUpdates()似乎停止了跳动,但后来我的细胞不会进行调整?

编辑:这仅在时出现底部UITableView.

Kár*_*rás 47

实际上我找到了一个很好的方法来解决这个问题..它让我疯狂但看起来:

那么你

  • 有一个可扩展内容的表
  • 想要为单元格的约束设置动画(例如高度)
  • 因此,您调用tableView.beginUpdates()和tableView.endUpdates()

桌子上跳起来......

正如其他人之前所说的,这是因为更新tableView会使tableView Scroll

解决方案?

我们假设您的代码如下所示:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath)
        cell.heightConstraint = 100
        UIView.animate(withDuration: 0.15, animations: {

            self.view.layoutIfNeeded()
            self.tableView.beginUpdates()
            self.tableView.endUpdates()
        }, completion: nil)
}
Run Code Online (Sandbox Code Playgroud)

然后要修复跳转问题,您必须保存当前tableView滚动位置,直到调用tableView.endUpdates().

像这样:

var currentScrollPos : CGFloat?

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // Force the tableView to stay at scroll position until animation completes
        if (currentScrollPos != nil){
            tableView.setContentOffset(CGPoint(x: 0, y: currentScrollPos!), animated: false)
        }
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath)
        cell.heightConstraint = 100
        UIView.animate(withDuration: 0.15, animations: {

            self.currentScrollPos = self.tableView.contentOffset.y

            self.view.layoutIfNeeded()
            self.tableView.beginUpdates()
            self.tableView.endUpdates()

            self.currentScrollPos = nil
        }, completion: nil)
}
Run Code Online (Sandbox Code Playgroud)

  • 经过3-4天的研究并摸不着头脑,有什么使我引荐到这篇文章,该文章为我提供了帮助:) (3认同)
  • 你救了我的一天!非常感谢! (3认同)

Mat*_*lak 6

在解决了这个问题之后,它似乎是表视图本身中的一个错误。调用endUpdates会触发一个委托,向其func scrollViewDidScroll(_ scrollView: UIScrollView)报告错误的内容偏移量。随后将其重置为原来的状态,但是第二个调用似乎是动画的,而第一个调用则没有。

经过进一步检查,这似乎仅在表视图向下滚动足以使其认为其总内容高度不足以使其内容偏移有效时才发生。我猜这可能是由于内容插图,自动行高和估计行高所致。

很难说出错误的确切位置,但让我们来解决问题:

对于那些使用自动行高的用户,快速解决方法很可能就是将估算的行高简单地增加到您期望的最大单元格大小。在大多数情况下,将其提高到极高的价值根本不会产生任何问题。

对于其余的无法更改估计的行高的文件,您可以通过在执行更新之前注入很大的页脚视图来解决此问题。请参阅以下代码以获取图片(注释应该是正在发生的事情的最好解释)。

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // Modify data source
        let currentResult = results[indexPath.row] // Fetch object
        let toggledResult = (object: currentResult.object, extended: !currentResult.extended) // Toggle being extended
        results[indexPath.row] = toggledResult // Assign it back to array

        // Find the cell
        if let cell = tableView.cellForRow(at: indexPath) as? ResultTableViewCell {
            let currentFooterView = tableView.tableFooterView // Steal the current footer view
            let newView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: tableView.bounds.width, height: tableView.bounds.height*2.0)) // Create a new footer view with large enough height (Really making sure it is large enough)
            if let currentFooterView = currentFooterView {
                // Put the current footer view as a subview to the new one if it exists (this was not really tested)
                currentFooterView.frame.origin = .zero // Just in case put it to zero
                newView.addSubview(currentFooterView) // Add as subview
            }
            tableView.tableFooterView = newView // Assign a new footer

            // Doing standard animations
            UIView.animate(withDuration: 0.3, animations: {
                cell.resultObject = toggledResult // This will trigger internal cell animations

                // Standard refresh
                tableView.beginUpdates()
                tableView.endUpdates()
                tableView.tableFooterView = currentFooterView // Put the footer view back
            })
        }

    }
Run Code Online (Sandbox Code Playgroud)

作为参考,这是没有跳转修复的代码:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // Modify data source
        let currentResult = results[indexPath.row] // Fetch object
        let toggledResult = (object: currentResult.object, extended: !currentResult.extended) // Toggle being extended
        results[indexPath.row] = toggledResult // Assign it back to array

        // Find the cell
        if let cell = tableView.cellForRow(at: indexPath) as? ResultTableViewCell {
            // Doing standard animations
            UIView.animate(withDuration: 0.3, animations: {
                cell.resultObject = toggledResult // This will trigger internal cell animations

                // Standard refresh
                tableView.beginUpdates()
                tableView.endUpdates()
            })
        }

    }
Run Code Online (Sandbox Code Playgroud)

请注意不要同时应用两个修复程序。滚动到底部并具有较大的估计高度和更改页脚会破坏整个逻辑,并且单元格会崩溃(将其大小调整为较小的高度)。因此,如果此时遇到问题,请检查估计的高度是否太高。


Doe*_*ata 5

您还可以通过使用UIView.performWithoutAnimation来防止动画

很简单:

UIView.performWithoutAnimation {
    self.tableView.beginUpdates()
    self.tableView.endUpdates()
}
Run Code Online (Sandbox Code Playgroud)

但是,如果使用此方法,我会注意到一些奇怪的UI问题。您可以使用以下扩展名重新加载tableView:

extension UITableView {
  func reloadWithoutScroll() {
    let offset = contentOffset
    reloadData()
    layoutIfNeeded()
    setContentOffset(offset, animated: false)
  }
}
Run Code Online (Sandbox Code Playgroud)

由于滚动位置已保存并在重新加载后重置,因此您不会看到任何“跳跃”。


归档时间:

查看次数:

5667 次

最近记录:

6 年,9 月 前