TableView w.Динамическая высота прокручивается при перезагрузке с новыми данными - PullRequest
0 голосов
/ 18 декабря 2018

Сводка Мой UITableView с динамической высотой ячеек случайным образом прокручивается при вставке новых ячеек сверху.

Подробное объяснение Как часть UITableViewController, чтобы избежать неточной прокруткикогда я запускаю reloadData () и прокручиваю, у меня есть следующая рабочая реализация, основанная на этом переполнении стека ответ :

var cellHeightsDictionary: [IndexPath: CGFloat] = [:]   

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {  
      cellHeightsDictionary[indexPath] = cell.frame.size.height  
}  

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {  
  if let height =  cellHeightsDictionary[indexPath] {  
      return height  
  }  
  return UITableView.automaticDimension  
}  

Это работает очень хорошо, за исключением добавления новых данных вверху висточник данных и запуск reloadData ().При этом я не нашел надлежащей реализации, которая предотвращает случайную прокрутку таблицы ... Это проблема, которую я пытаюсь решить.

Что я пробовал

  1. Я попытался использовать разницу между contentSize до и после обновления, а затем обновить позицию прокрутки.К сожалению, кажется, основываясь на том, что я прочитал в документации Apple, что при использовании ячеек Dynamic, contentSize нельзя доверять для этой цели и у меня не работает.Тем не менее, для справки, вот одна из моих реализаций.

    UIView.performWithoutAnimation { //Detect exact position of the cell in relation to navBar let navBar = navigationController?.navigationBar let cellRectInTable = tableView.rectForRow(at: IndexPath(row: topArticle + countOfAddedItems, section: 0)) let positionVsNavBar = tableView.convert(cellRectInTable.origin, to: navBar) let positionVsTop = tableView.convert(cellRectInTable.origin, to: tableView.superview) let offsetOfCell = positionVsTop.y - positionVsNavBar.y tableView.reloadData() tableView.scrollToRow(at: IndexPath(row: topArticle + countOfAddedItems, section: 0), at: .top, animated: false) tableView.contentOffset.y = tableView.contentOffset.y + offsetOfCell }

  2. Я попытался установить tableView.estimatedRowHeight = 0, tableView.estimatedSectionHeaderHeight = 0 и tableView.estimatedSectionFooterHeight =0 до добавления, но, похоже, это никак не повлияло.

  3. Подход, который, казалось, работал лучше всего, был, когда я получал высоты ячеек из cellHeightsDictionary и суммировал их на основеколичество дополнений.Это работало иногда безупречно, но иногда нет.Кроме того, при добавлении большого количества строк может произойти сбой, так как не было найдено значение в cellHeightsDictionary.Я попробовал несколько вариантов этого, но не получил его на работу.Не менее важно, я не уверен, почему именно это дало мне лучшие результаты, поэтому я могу упустить что-то очевидное.У меня есть ощущение, что где-то в этой последней реализации есть ответ, но я пока не смог его найти.

    var iteratorCounter = 0
    var heightCum = CGFloat (integerLiteral: 0)
    , в то время как iteratorCounter heightCum = heightCum + cellHeightsDictionary [IndexPath (row: iteratorCounter, section: 0)]!
    iteratorCounter = iteratorCounter + 1
    }

    UIView.performWithoutAnimation {  
       tableView.reloadData()  
       tableView.layoutIfNeeded()  
       tableView.contentOffset.y = tableView.contentOffset.y + heightCum   
    }
    

Я перепробовал все, что мог придумать, и искал везде, о чем думал, безуспешно.Любая помощь приветствуется

Спасибо за миллион!

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Определите переменную:

 var itemHeights = [CGFloat](repeating: UITableViewAutomaticDimension, count: 1000)

Количество может быть изменено в соответствии с вашими требованиями

и обновите методы tableView

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if itemHeights[indexPath.row] == UITableViewAutomaticDimension {
            itemHeights[indexPath.row] = cell.bounds.height
        }
        // fetch more cells, etc.
    }

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return itemHeights[indexPath.row]
    }
0 голосов
/ 30 декабря 2018

Подход, который в итоге работал гладко, был:

Шаг 1. Сохранение в переменной UITableViewCell под панелью навигации и сохранение смещения этой ячейки относительно панели навигации.

Шаг 2. Вставьте ячейки / reloadData.

Шаг 3. Перейдите к ячейке, которую мы сохранили перед вставкой / перезагрузкой, а затем добавьте смещение.

Вот код:

UIView.performWithoutAnimation {
    //Step 1 Detect & Store
    let topWillBeAt = getTopVisibleRow() + countOfAddedItems
    let oldHeightDifferenceBetweenTopRowAndNavBar = heightDifferenceBetweenTopRowAndNavBar()

    //Step 2 Insert
    self.tableView.insertRows(at: arrayOfIndexPaths, with: .none)

    //Step 3 Restore Scrolling
    tableView.scrollToRow(at: IndexPath(row: topWillBeAt, section: 0), at: .top, animated: false)
    tableView.contentOffset.y = tableView.contentOffset.y - oldHeightDifferenceBetweenTopRowAndNavBar


}

И вспомогательные функции:

  func getTopVisibleRow () -> Int {
        //We need this to accounts for the translucency below the nav bar
        let navBar = navigationController?.navigationBar
        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)
        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height + 1)
        let accurateIndexPath = tableView.indexPathForRow(at: pointWhereNavBarEnds)
        return accurateIndexPath?.row ?? 0
    }

    func heightDifferenceBetweenTopRowAndNavBar()-> CGFloat{
        let rectForTopRow = tableView.rectForRow(at:IndexPath(row:  getTopVisibleRow(), section: 0))
        let navBar = navigationController?.navigationBar
        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)
        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height)
        let differenceBetweenTopRowAndNavBar = rectForTopRow.origin.y - pointWhereNavBarEnds.y
        return differenceBetweenTopRowAndNavBar


    }
0 голосов
/ 18 декабря 2018

В качестве последнего средства вы можете встроить табличное представление в новое представление прокрутки и отключить прокрутку табличного представления.Вы можете полностью контролировать режим прокрутки (хотя и все программно).

В этом случае мы должны добавить ограничение макета, соответствующее высоте представления таблицы, и выход для этого ограничения.

Если наш вид ячейки имеет соответствующую компоновку, мы можем рассчитать точную высоту ячейки с помощью метода systemLayoutSizeFittingSize.Если ячейка имеет несколько меток (например, динамическое содержимое), попробуйте выполнить вычисления с фиксированной шириной (добавьте явное ограничение ширины в ячейку).

Имея это, мы можем перезагрузить табличное представление, как показано ниже:
1) вставить данные в источник данных
2) пересчитать высоту содержимого
3) изменить константу ограничения высоты
4) вызвать layoutIfNeeded
5) перезагрузить представление таблицы
6) переместить содержимое прокруткисмещение соответственно => setContentOffset(_ contentOffset: CGPoint, animated: Bool)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...