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

时间:2015-11-18 20:31:44

标签: ios uitableview

问题:当我在tableView.beginUpdates()底部时(仅在底部)和tableView.endUpdates() / { {1}}被调用,UITableView稍微跳了起来。我不希望它这样做。

设置UITableView UITableViewCells 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() 之间的内容的代码:

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()
}

我的观点WillAppear:

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

    return UITableViewAutomaticDimension

}

我的身高ForRow:

cell!.setNeedsUpdateConstraints()

更新:从cell!.updateConstraintsIfNeeded() / cell!.setNeedsLayout()删除cell!.layoutIfNeeded(tableView.beginUpdates()tableView.endUpdates()UITableView)似乎停止了跳跃,但后来我的细胞没有调整大小...

修改:只有在{{1}}的底部时才会出现。

4 个答案:

答案 0 :(得分:30)

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

所以你

  • 拥有可扩展内容的表格
  • 想要制作单元格约束的动画(例如高度)
  • 因此你调用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)
}

然后要解决跳转问题,你必须保存当前的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)
}

答案 1 :(得分:5)

在玩这个问题之后,它似乎是表格视图本身的一个错误。调用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
            })
        }

    }

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

    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()
            })
        }

    }

请注意,不要同时应用这两个修补程序。当滚动到底部并且单元格折叠(调整到较小的高度)时,具有较大的估计高度加上改变的页脚将破坏整个逻辑。因此,如果您在此时遇到问题,请检查您的估计身高是否过高。

答案 2 :(得分:0)

您还可以使用UIView.performWithoutAnimation

来防止动画

这很简单:

UIView.performWithoutAnimation {
    self.tableView.beginUpdates()
    self.tableView.endUpdates()
}

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

extension UITableView {
  func reloadWithoutScroll() {
    let offset = contentOffset
    reloadData()
    layoutIfNeeded()
    setContentOffset(offset, animated: false)
  }
}

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

答案 3 :(得分:-1)

应该很简单:

UITableView继承自UIScrollView。在更新之前,使用UIScrollView的方法获取当前滚动偏移量。更新后,将偏移量应用于表视图。

就是这样。