Подтвердить что ты не робот

UITableView падает, если источник данных обновляется во время прокрутки

У меня есть приложение, в котором источник данных для UITableView обновляется фоновым потоком с удаленного сервера каждые 30 секунд.

Авария возникает, если пользователь прокручивает tableView или если tableView находится в процессе reloadTableView:. Причиной аварии является то, что количество строк в таблице во время сбоя не совпадает с количеством строк в момент начала перерисовывания.

Другая ошибка возникает, когда запрашиваемая ячейка TableView выходит за пределы диапазона, потому что между тем, как вызывается время numberofTableViewCells: и вызывается время cellfForRowAtIndexPath, датамодель изменилась и ячейка больше не существует.

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

Как заблокировать tableView от прокрутки или перезагрузки при обновлении tableViewDataSource? Какова наилучшая практика для такого рода ситуаций?

Спасибо.

4b9b3361

Ответ 1

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

Однако, с точки зрения дизайна пользовательского интерфейса, наличие активного обновления таблицы во время прокрутки пользователя будет дезориентировать пользователя. Пользователь будет думать, что они находятся в верхней/средней/нижней части таблицы, а затем внезапно оказываются в нижней/верхней/средней. Пользователь будет думать, что они просмотрели все данные в одном разделе таблицы, но в действительности таблица добавит что-то, что им нужно было увидеть. Например, если таблица представляет собой список алфавитных имен, пользователь проверяет все имена, смотрящие на "U", видит, что их нет, а затем идет, чтобы проверить где-то еще в таблице. Между тем, таблица невидимо обновляет раздел "U" со свежими именами, пока пользователь смотрит в другое место. Даже если пользователь понимает, что таблица динамически обновляется (чего больше всего нет), им придется постоянно прокручивать, проверяя практически всю таблицу, чтобы увидеть, что изменилось.

Лучший дизайн пользовательского интерфейса - дать пользователю возможность обновлять. Поместите в панель кнопку "Обновить" или "Новые данные", а затем установите ее, когда появятся новые данные. После нажатия кнопки заморозить таблицу, обновить и только затем возобновить взаимодействие пользователя. Также неплохо было бы визуально обозначить добавленные строки.

Это сделает пользовательский интерфейс более понятным для пользователя и одновременно устранит вашу проблему.

Edit01:

Если вы не используете Core Data, чтобы реализовать живое и невидимое обновление таблицы. вам необходимо заморозить в вызовах – tableView:numberOfRowsInSection или – numberOfSectionsInTableView: до вызова – tableView:cellForRowAtIndexPath:.

Так как – tableView:numberOfRowsInSection вызывается первым, я бы поместил там вызов для первого обновления и затем заморозил вашу модель данных. Таким образом, модель данных вернет правильное количество разделов и строк.

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

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

Если вы используете Core Data, вы можете использовать NSFetchedResultController, и методы делегирования сообщают вам, когда модель данных изменится. Он должен вернуть информацию в соответствующем разделе и строке, обновленную в реальном времени. Это довольно легко управлять таблицей обновления таким образом. Однако он не справится с проблемой ввода данных в модель так быстро, что модель изменится между вызовами метода. Вам все равно придется замораживать и/или замедлять работу модели. Однако вам не нужен таймер.

Core Data - ваш лучший вариант, но даже это будет сложно реализовать, потому что вы пытаетесь что-то сделать против зерна пользовательского интерфейса, и поэтому API не поддерживает его.

Обновление:

Оглядываясь назад на этот ответ, я вижу, что я забыл упомянуть [UITableView beginUpdates], который заморозит конфигурацию таблицы, когда строки будут добавлены или удалены. Он сопряжен с [UITableView endUpdates], чтобы включить изменения в пользовательский интерфейс.

Ответ 2

Небезопасно вызывать методы для объектов GUI (например, UITableView) из фонового потока.

Вам нужно сделать что-то вроде:

[ tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]

См. uitableview-drawing-problems-when-reloaddata-is-called