Заголовки разделов подпрыгивают при перезагрузке UITableView

У меня есть UITableView, который подвергается пакетному обновлению при изменении состояния приложения. Изменение может повлиять на несколько строк/разделов или только на один раздел (но на 1 или более строк).

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

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

Видеозахват

Эти разделы не являются частью пакетного обновления.

UITableView/Controller настраивается с помощью раскадровки, предназначенной для iOS 11. UITableView использует расчетную высоту строк и секций, а ячейки размещаются с использованием автоматического макета.

Свойства раскадровки Xcode

UITableViewController использует более крупную модель данных, которая управляет операциями состояния и изменения состояния. API гарантирует, что требуемые обновления рассчитываются на основе требуемого состояния модели (т. е. удаления и обновления относятся к предыдущему состоянию, вставки относятся к новому состоянию).

Последним шагом является применение рассчитанных изменений к табличному представлению посредством процесса пакетного обновления...

tableView.beginUpdates()

// Calculate the rows/sections which need to be changed

if let ops = operation.sections[.delete], ops.count > 0 {
    tableView.deleteSections(ops, with: deleteSectionAnimation)
}
if let ops = operation.sections[.update], ops.count > 0 {
    tableView.reloadSections(ops, with: reloadSectionAnimation)
}
if let ops = operation.sections[.insert], ops.count > 0 {
    tableView.insertSections(ops, with: insertSectionAnimation)
}

if let ops = operation.rows[.delete], ops.count > 0 {
    tableView.deleteRows(at: ops, with: deleteRowAnimation)
}
if let ops = operation.rows[.update], ops.count > 0 {
    tableView.reloadRows(at: ops, with: reloadRowAnimation)
}
if let ops = operation.rows[.insert], ops.count > 0 {
    tableView.insertRows(at: ops, with: insertRowAnimation)
}
tableView.endUpdates()

tableView.reloadData()
tableView.layoutIfNeeded()

Свойства анимации строк и разделов (например, deleteSectionAnimation) установлены на automatic. Я попытался установить анимацию раздела на none без каких-либо различий.

Я пытался удалить reloadData и layoutIfNeeded в различных комбинациях, и хотя они создают другие проблемы, они, похоже, не приближаются к решению этой проблемы.

Я заметил одну вещь: это происходит только тогда, когда обновления, происходящие с UITableView, выполняются за пределами экрана.

Я потратил некоторое время, пытаясь найти решение проблемы (например, не звонить reloadData), но не могу найти что-то, что работает.

Есть ли какое-то другое свойство, функция, на которую я должен обратить внимание, или порядок, в котором выполняются обновления, неверен?

Примечания

  • Это статическая таблица, настроенная с помощью раскадровки.
  • Строки и разделы добавляются/удаляются во время изменения состояния, что, по-видимому, вызывает проблемы/изменения в представлении прокрутки.
  • Хотя отключение анимации (с помощью UIView.setAnimationsEnabled), кажется, решает проблему, это нежелательное решение, поскольку оно удаляет анимацию для всех обновлений (на экране или вне экрана).
  • Проблема возникает только тогда, когда представление прокрутки НЕ находится в верхней части представления, обновления/анимации, происходящие, когда представление таблицы прокручивается до самого верха, не вызывают проблем.
  • Я пытался использовать indexPathsForVisibleRows, чтобы определить, происходят ли обновления на экране или за его пределами, и отключить анимацию с помощью UIView.setAnimationsEnabled, но это не решает проблему, если обновление происходит на экране, а табличное представление не прокручивается вверх. просмотра прокрутки

person MadProgrammer    schedule 07.11.2017    source источник
comment
вы пытались установить UIView.setAnimationsEnabled на false ? а затем установите его обратно в `true после того, как вы закончите обновление? В противном случае, если вы уже пробовали это, можете ли вы попробовать создать небольшой проект, который показывает эту проблему?   -  person TNguyen    schedule 08.11.2017
comment
@TNguyen Я пытался использовать UIView.setAnimationsEnabled, хотя это и решает проблему, но, очевидно, останавливает все остальные анимации, которые я хочу. Я искал решение для использования UIView.setAnimationsEnabled, когда обновления находятся за пределами экрана, но поскольку управление состоянием безумно сложно, каждый раз, когда обновление также происходит в видимой области, когда табличное представление не находится в верхней части экрана. просмотр прокрутки, я получаю те же проблемы, что и описанные выше   -  person MadProgrammer    schedule 09.11.2017
comment
Не уверен, что это сработает, так как я никогда не использовал его раньше, но после прочтения документации это может быть полезно, возможно, попробуйте окружить его в UIView.performWithoutAnimation   -  person TNguyen    schedule 09.11.2017
comment
@TNguyen Я почти уверен, что это просто убедительный аргумент для setAnimationEnabled. К сожалению, отключить анимацию на самом деле невозможно (поскольку они мне нужны, когда на экране появляются обновления). Я реализовал версию этого решения, и оно решена проблема с подпрыгиванием прокрутки во время обновлений, но заголовки разделов по-прежнему подпрыгивают/двигаются   -  person MadProgrammer    schedule 09.11.2017
comment
@TNguyen Основные проблемы, по-видимому, связаны с использованием автоматического макета и предполагаемой высоты ячеек / разделов.   -  person MadProgrammer    schedule 09.11.2017
comment
У меня была точно такая же проблема. Я понял это, хотя. Для меня проблема заключалась в том, что другие ячейки (не заголовок раздела) не имели правильной предполагаемой высоты.   -  person Gabriel Pires    schedule 06.04.2018
comment
@GabrielPires Я думаю (часть более крупной проблемы) в том, что почти ни одна из ячеек не имеет одинаковой высоты.   -  person MadProgrammer    schedule 18.04.2018
comment
@MadProgrammer Я делаю что-то очень похожее на ответ в этом сообщении SO (stackoverflow.com/questions/41342777/), чтобы получить точную предполагаемую высоту. Я сохраняю высоту ячейки из willDisplay в словарь, где ключ — indexPath, а значение — высота. Затем в оцениваемомHeightForRowAt я беру сохраненные высоты. Я не уверен, насколько этично делать это так, но у меня это сработало в нескольких приложениях.   -  person Gabriel Pires    schedule 18.04.2018
comment
@GabrielPires Я делал что-то подобное, будь то куча вещей - вероятно, я делал это неправильно ... также поддержка iOS 9 затрудняет, хотя спасибо - я еще раз взгляну на это   -  person MadProgrammer    schedule 18.04.2018