TableView вычисляет неправильно оцененный HeightForRowAt - PullRequest
0 голосов
/ 18 января 2019

Я создаю приложение, похожее на чат, в котором tableView отображает ячейки с динамической высотой.


Ячейки имеют свои представления и подпредставления, правильно ограниченные

Так что AutoLayout может предсказать высоту клеток

(верх, низ, ведущий, трейлинг)


Но все же - как вы можете видеть на видео - полоса прокрутки показывает, что были вычислены неправильные высоты:

Пересчитывает высоту при появлении новой строки.

Видео: https://youtu.be/5ydA5yV2O-Q

(со второй попытки прокрутить вниз все нормально)


Код:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

Это простая проблема. Кто-нибудь может мне помочь?

Обновление 1.0

Добавлен github:

https://github.com/krptia/Test

Ответы [ 10 ]

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

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

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

enter image description here

Просто удалите выделенный вид из UITableView, и он будет работать как шарм.

enter image description here

Надеюсь, это поможет.

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

Надеюсь, вы много слышали об этом.поэтому сделайте небольшой перерыв и вернитесь на стол и сделайте 2 - 3 шага для этого шага.

1) Убедитесь в правильности настройки Autolayouts метки Cell, как показано ниже.

enter image description here

2) Число строк UILabel установлено равным нулюдля динамической высоты текста.

enter image description here

3) установить автоматическую размерную высоту ячейки.

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

, и я считаю, что она должнабыть работой.см. результаты моего кода.

enter image description here

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

Что вы хотите сделать, это удалить лишние пустые ячейки . Вы можете сделать это, установив tableFooterView в пустое значение UIView в методе viewDidLoad. Я клонировал код с вашего GitHub и изменил метод:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")
    tableView.tableFooterView = UIView()
}

установка tableFooterView на nil работала и у меня

tableView.tableFooterView = nil

enter image description here

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

Но все же - как вы можете видеть на видео - индикаторная полоса прокрутки показывает, что были вычислены неправильные высоты:

Итак, вам нужна точная высота содержимого.

Для этой цели вы не можете использовать статический estimatedRowHeight. Вы должны реализовать более правильную оценку, как показано ниже.

    ...

    var sampleCell: WorldMessageCell?

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")

        sampleCell = UINib(nibName: "WorldMessageCell", bundle: nil).instantiate(withOwner: WorldMessageCell.self, options: nil)[0] as? WorldMessageCell
    }

    ...

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        if let cell = sampleCell {
            let text = self.textForRowAt(indexPath)
            // note: this is because of "constrain to margins", which value is actually set after estimation. Do not use them to remove below
            let margin = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
            // without "constrain to margins"
            // let margin = cell.contentView.layoutMargins 
            let maxSize = CGSize(width: tableView.frame.size.width - margin.left - margin.right,
                                 height: CGFloat.greatestFiniteMagnitude)
            let attributes: [NSAttributedString.Key: Any]? = [NSAttributedString.Key.font: cell.messageLabel.font]
            let size: CGRect = (text as NSString).boundingRect(with: maxSize,
                                                                 options: [.usesLineFragmentOrigin], attributes: attributes, context: nil)
            return size.height + margin.top + margin.bottom
        }
        return 100
    }

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

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

Согласно вашему ответу на мой комментарий, когда вы установите

оценкаHeightForRowAt и heightForRow при тех же значениях, что и у работа

Я могу подтвердить, что вы правы и что проблема в том, что AutoLayout не может рассчитать правильное значение для estimatedHeightForRowAt. Таким образом, в основном есть две возможные вещи:

  • найти альтернативный макет, который даст лучшие результаты
  • сделайте свой собственный расчет для оцененного HeightForRowAt, который даст более точные результаты (как правило, вы должны быть в состоянии определить ожидаемую высоту на длину текста, а затем добавить поля к этой фигуре - вам нужно приложить немного усилий, чтобы найти правильная математика, но она должна работать).
0 голосов
/ 21 января 2019

Сконфигурируйте ваш просмотр таблицы с этими значениями в viewDidLoad()

        tableView.estimatedRowHeight = 100.0
        tableView.rowHeight = UITableView.automaticDimension
        tableView.tableFooterView = UIView()

И вы должны удалить оба метода источника данных о высоте.

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

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

посмотреть это видео

загрузите раскадровку и добавьте ее в свой проект, а затем отметьте

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

Проблема с вашим estimatedHeightForRowAt методом. Как следует из названия, он дает приблизительную высоту таблицы, чтобы иметь представление о прокручиваемом контенте до тех пор, пока не отобразится фактический контент. Более точное значение приведет к более плавной прокрутке и оценке высоты.

Вы должны установить это значение на достаточно большое, чтобы оно могло представлять высоту вашей ячейки с максимальным содержанием. В вашем случае 650 работает нормально.

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return 650
}

Результат был бы намного лучше при таком подходе.

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

tableView.estimatedRowHeight = 650.0
tableView.rowHeight = .automaticDimension

Оптимизация

Еще одна вещь, которую я заметил в вашем демо-проекте. Вы использовали слишком много if-else в своем cellForRowAtIndexPath, что делает его немного медленнее. Попробуйте минимизировать это. Я сделал некоторые уточнения, и это улучшает производительность.

  1. Определите массив, содержащий текст вашего сообщения.

    var messages = ["Lorem ipsum,"many more",.....]

  2. Замените ваш cellForRowAt indexPath следующим:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { var cell : WorldMessageCell cell = tableView.dequeueReusableCell(withIdentifier: "WorldMessageCell", for: indexPath) as! WorldMessageCell if indexPath.row < 14 { cell.messageLabel.text = messages[indexPath.row] } else if indexPath.row >= 14 && indexPath.row != 27 { cell.messageLabel.text = messages[14] } else if indexPath.row == 27 { cell.messageLabel.text = messages.last } return cell }

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

Вам нужно установить tableFooterView на пустое.

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.tableFooterView = UIView()
    // your staff
}
...