TableView崩溃时滚动到另一个单元格:奇怪的行为

时间:2019-01-20 16:52:52

标签: ios swift tableview cell

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


如果character countmore than 250,则气泡会缩短

如果用户点击气泡

  • 先前的选择被取消选择(缩短)
  • 新选择扩展并显示了全部内容
  • 新选择的顶部约束将(从0更改为4)

我想实现什么?

  

如果已经选择了一个长气泡,但是用户选择了另一个气泡,我希望tableView滚动到新选择的气泡的位置。

我将分享有关它的视频

如果不进行滚动,则contentOffset保持不变,并且看起来很糟。

(在视频中:右侧)


视频:

  

:没有提到的滚动

     

:滚动

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

5 个答案:

答案 0 :(得分:1)

首先让我们定义“不滚动”的含义-我们的意思是单元格更少或更多保持不变。因此,我们想要找到一个我们想要成为锚点单元的单元。从更改之前到更改之后,从单元格顶部到屏幕顶部的距离是相同的。

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

动画可能有点奇怪,因为同时进行了很多操作。我们将同时更改内容偏移量和单元格的大小,但是如果您将手指放在第一个可见的单元格旁边,则会看到它的正确位置。

答案 1 :(得分:0)

尝试在willDisplay上使用此UITableViewDelegate api

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if (indexPath == currentSelectedIndexPath) {
        // logic to expand your cell
        // you don't need to animate it since still not visible
    }
}

答案 2 :(得分:0)

使用此代码替换bubbleTappedHandler的代码,然后运行并检查:

var headersConfig = { 
    headers:  {
            'Authorization': 'Basic ' + btoa(dbUser + ' : ' + dbPass)
        }
    };
return $http.get(url, headersConfig);

答案 3 :(得分:0)

您可以使用 tableView?.scrollToRow 。例如

let newIndexPath = IndexPath(row: 0, section: 0) // put your selected indexpath and section here 
yourTableViewName?.scrollToRow(at: newIndexPath, at: UITableViewScrollPosition.top, animated: true) // give the desired scroll position to tableView 

可用的滚动位置为。无 .top 。中间 .bottom

答案 4 :(得分:0)

此故障是由于一个小错误。只需完成这两项操作,问题便会解决。

  1. 在`viewDidLoad

    中设置tableView的rowHeightestimatedRowHeight
    tableView.estimatedRowHeight = 316 // Value taken from your project. This may be something else
    tableView.rowHeight = UITableView.automaticDimension
    
  2. 从您的代码中删除estimatedHeightForRowAtheightForRowAt委托方法。