Свертывание ячейки TableView при прокрутке на другую: странное поведение - PullRequest
0 голосов
/ 20 января 2019

У меня есть tableView с пузырьками чата.


Эти пузырьки укорачиваются, если character count равно more 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

Я сделал небольшойверсия моего приложения, так что вы можете увидеть и проверить, в чем моя проблема.Я был бы очень благодарен, если бы кто-то мог помочь решить эту проблему!С

Ответы [ 5 ]

0 голосов
/ 29 января 2019

Сначала давайте определим, что мы подразумеваем под «без прокрутки» - мы имеем в виду, что клетки, более или менее, остаются неизменными.Итак, мы хотим найти ячейку, которой хотим быть якорной.До и после изменений расстояния от верха ячейки до верха экрана одинаковы.

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

Анимация может быть немного странной, потому что в то же время происходит много всего.Мы одновременно изменяем смещение содержимого и размеры ячейки, но если вы поместите палец рядом с первой ячейкой, которая видна сверху, вы увидите, что она оказывается в правильном месте.

0 голосов
/ 23 января 2019

Этот глюк из-за маленькой ошибки.Просто сделайте эти 2 вещи, и проблема будет решена.

  1. Установите tableView's rowHeight и estimatedRowHeight в `viewDidLoad

    tableView.estimatedRowHeight = 316 // Value taken from your project. This may be something else
    tableView.rowHeight = UITableView.automaticDimension
    
  2. Удалите из вашего кода методы делегатов estimatedHeightForRowAt и heightForRowAt.

0 голосов
/ 23 января 2019

Замените ваш код для bubbleTappedHandler этим, запустите и проверьте:

func bubbleTappedHandler(sender: UITapGestureRecognizer) {

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

        if indexPath == currentSelectedIndexPath {
            currentSelectedIndexPath = nil
            tableView.reloadRows(at: [indexPath], with: .automatic)
        } else {
            if (currentSelectedIndexPath != nil){
                if let prevSelectedIndexPath = currentSelectedIndexPath {
                    currentSelectedIndexPath = indexPath
                    tableView.reloadRows(at: [prevSelectedIndexPath, indexPath], with: .automatic)
                }
            } else {
                currentSelectedIndexPath = indexPath
                tableView.reloadRows(at: [indexPath], with: .automatic)
            }

        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { [weak self] in
            let currentIndexPath = self?.currentSelectedIndexPath ?? indexPath
            self?.tableView.scrollToRow(at: currentIndexPath, at: .top, animated: false)
        })
    }
}
0 голосов
/ 23 января 2019

Вы можете использовать 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 , .middle , .bottom

0 голосов
/ 21 января 2019

Попробуйте использовать willDisplay API на UITableViewDelegate

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
    }
}
...