ios – TableView在滚动到另一个时折叠单元格:奇怪的行为

我有一个带聊天气泡的tableView.

如果字符数超过250,则会缩短这些气泡

如果用户点击了气泡

>取消选择(缩短)先前的选择
>新选择扩展并显示整个内容
>新选择顶部约束更改(从0到4)

我想要实现什么?

If a long bubble is selected already, but the user selects another bubble, I want the tableView to scroll to the position of the new selected bubble.

我会分享一个关于它的视频

没有这个滚动,contentOffset保持不变,看起来很糟糕.

(在视频中:右侧)

视频:

Right: without the mentioned scrolling

Left: with scrolling

https://youtu.be/_-peZycZEAE

问题出现了:

在左边,你可以注意到它是毛病.

>随机幽灵细胞无缘无故地出现.
>有时甚至会弄乱一些气泡的高度(不在视频中)

为什么会这样?

码:

func bubbleTappedHandler(sender: UITapGestureRecognizer) {

        let touchPoint = sender.location(in: self.tableView)
        if let indexPath = tableView.indexPathForRow(at: touchPoint) {

            if indexPath == currentSelectedIndexPath {

                // Selected bubble is tapped, deselect it
                self.selectDeselectBubbles(deselect: indexPath)

            } else {
                if (currentSelectedIndexPath != nil){

                    // Deselect old bubble, select new one
                    self.selectDeselectBubbles(select: indexPath, deselect: currentSelectedIndexPath)

                } else {

                    // Select bubble
                    self.selectDeselectBubbles(select: indexPath)

                }
            }

        }
    }



    func selectDeselectBubbles(select: IndexPath? = nil, deselect: IndexPath? = nil){


        var deselectCell : WorldMessageCell?
        var selectCell : WorldMessageCell?

        if let deselect = deselect {
            deselectCell = tableView.cellForRow(at: deselect) as? WorldMessageCell
        }
        if let select = select {
            selectCell = tableView.cellForRow(at: select) as? WorldMessageCell
        }


        // Deselect Main
        if let deselect = deselect, let deselectCell = deselectCell {

            tableView.deselectRow(at: deselect, animated: false)
            currentSelectedIndexPath = nil
            // Update text
            deselectCell.messageLabel.text = self.dataSource[deselect.row].message.shortened()


        }
        // Select Main
        if let select = select, let selectCell = selectCell {

            tableView.selectRow(at: select, animated: true, scrollPosition: .none)
            currentSelectedIndexPath = select
            // Update text
            deselectCell.messageLabel.text = self.dataSource[select.row].message.full()
        }


        UIView.animate(withDuration: appSettings.defaultAnimationSpeed) {

            // Deselect Constraint changes

            if let deselect = deselect, let deselectCell = deselectCell {
                // Constarint change
                deselectCell.nickNameButtonTopConstraint.constant = 0
                deselectCell.timeLabel.alpha = 0.0
                deselectCell.layoutIfNeeded()

            }

            // Select Constraint changes
            if let select = select, let selectCell = selectCell {

                // Constarint change
                selectCell.nickNameButtonTopConstraint.constant = 4
                selectCell.timeLabel.alpha = 1.0
                selectCell.layoutIfNeeded()


            }


        }

        self.tableView.beginUpdates()
        self.tableView.endUpdates()



        UIView.animate(withDuration: appSettings.defaultAnimationSpeed) {
            if let select = select, deselect != nil, self.tableView.cellForRow(at: deselect!) == nil && deselectCell != nil {

                // If deselected row is not anymore on screen
                // but was before the collapsing,
                // then scroll to new selected row  

                self.tableView.scrollToRow(at: select, at: .top, animated: false)
            }
        }

    }

更新1:添加了Github项目

链接:https://github.com/krptia/test2

我制作了一个小版本的应用程序,以便您可以查看并测试我的问题所在.如果有人能帮忙解决这个问题,我会非常感激! :C

最佳答案 首先让我们通过“不滚动”来定义我们的意思 – 我们的意思是更少的细胞保持不变.所以我们想找到一个我们想成为锚细胞的细胞.从更改之前到更改之后,从单元格的顶部到屏幕顶部的距离是相同的.

var indexPathAnchorPoint:IndexPath?
var offsetAnchorPoint:CGFloat?

func findHighestCellThatStartsInFrame() -> UITableViewCell? {
  var anchorCell:UITableViewCell?
  for cell in self.tableView.visibleCells {
    let topIsInFrame = cell.frame.origin.y >= self.tableView.contentOffset.y
    if topIsInFrame {

      if let currentlySelected = anchorCell{
        let isHigerUpInView = cell.frame.origin.y < currentlySelected.frame.origin.y
        if  isHigerUpInView {
          anchorCell = cell
        }
      }else{
        anchorCell = cell    
      }
    }
  }
  return anchorCell
}

func setAnchorPoint() {
  self.indexPathAnchorPoint = nil;
  self.offsetAnchorPoint = nil;

  if let cell = self.findHighestCellThatStartsInFrame() {
    self.offsetAnchorPoint = cell.frame.origin.y - self.tableView.contentOffset.y
    self.indexPathAnchorPoint = self.tableView.indexPath(for: cell)
  }
}

我们在开始做之前先调用它.

 func bubbleTappedHandler(sender: UITapGestureRecognizer) {
    self.setAnchorPoint()
     ....

接下来,我们需要在进行更改后设置内容偏移量,以便单元格移回到它所设想的位置.

func scrollToAnchorPoint() {
  if let indexPath = indexPathAnchorPoint, let offset = offsetAnchorPoint {
    let rect = self.tableView.rectForRow(at: indexPath)
    let contentOffset = rect.origin.y - offset
    self.tableView.setContentOffset(CGPoint.init(x: 0, y: contentOffset), animated: false)
  }
}

接下来我们在完成更改后调用它.

  self.tableView.beginUpdates()
  self.tableView.endUpdates()
  self.tableView.layoutSubviews()
  self.scrollToAnchorPoint()

动画可能有点奇怪,因为有很多东西在同一时间发生.我们正在同时更改内容偏移量和单元格的大小,但如果您将手指放在第一个单元格旁边,顶部可见,您将看到它最终位于正确的位置.

点赞