У меня есть приложение, где источник данных для UITableView обновляет фоновым потоком от удаленного сервера каждые 30 секунд.
Катастрофический отказ происходит, если пользователь прокручивает tableView или если tableView в процессе reloadTableView:
. Причина катастрофического отказа состоит в том, что количество строк в таблице во время катастрофического отказа не соответствует количеству строк в то время запущенная перерисовка.
Другой катастрофический отказ происходит, когда требуемая ячейка TableView вне диапазона потому что между временем numberofTableViewCells:
назван и время cellfForRowAtIndexPath
назван, модель данных изменилась, и ячейка больше не там.
Данные для tableView обновляются от фонового потока. В то время как данные загружаются из сервера, пользователь должен все еще быть в состоянии взаимодействовать с tableView, но прямо сейчас который вызывает катастрофический отказ.
Как я блокирую tableView от прокрутки или перезагрузки, в то время как я обновляю tableViewDataSource? Какова лучшая практика для этого вида ситуации?
Спасибо.
Короткий ответ заключается в том, что вам нужно буферизовать данные в вашей модели данных и обновлять таблицу новыми данными только тогда, когда таблица не прокручивается. Это необходимо сделать в модели данных, потому что делегат источника данных не знает, что находится внутри модели данных или что изменилось.
Однако с точки зрения дизайна пользовательского интерфейса активное обновление таблицы во время прокрутки пользователя дезориентирует пользователя. Пользователь подумает, что он находится в верхней / средней / нижней части таблицы, а затем внезапно окажется в нижней / верхней / средней части. Пользователь будет думать, что он просмотрел все данные в одном разделе таблицы, но на самом деле таблица добавит то, что им нужно увидеть. Например, если таблица представляет собой список имен в алфавитном порядке, пользователь проверяет все имена, начинающиеся с буквы «U», видит, что их нет, а затем переходит к проверке в другом месте таблицы. Между тем, таблица незаметно обновляет раздел «U» новыми именами, пока пользователь ищет в другом месте. Даже если пользователь понимает, что таблица динамически обновляется (что в большинстве случаев не обновляется), ему придется постоянно прокручивать, проверяя практически всю таблицу, чтобы увидеть, что изменилось.
Лучший дизайн пользовательского интерфейса - дать пользователю возможность обновлять. Поместите кнопку «Обновить» или «Доступны новые данные» на панели, а затем настройте ее отображение при поступлении новых данных. После нажатия кнопки заморозьте таблицу, обновите и только после этого позвольте пользователю возобновить взаимодействие. Также было бы неплохо визуально пометить добавленные строки.
Это сделает пользовательский интерфейс более понятным для пользователя и одновременно решит проблему сбоя.
Если вы не используете Core Data, чтобы реализовать живое и невидимое обновление таблицы. вам нужно будет заморозить в вызовах - tableView: numberOfRowsInSection
или - numberOfSectionsInTableView:
перед вызовом - tableView: cellForRowAtIndexPath:
.
Так как - tableView: numberOfRowsInSection
вызывается первым, я бы поместил туда вызов, чтобы сначала обновить, а затем заморозить модель данных. Таким образом, модель данных вернет правильное количество разделов и строк.
Я полагаю, вам придется разделить модель данных на две части, одна из которых будет буферизовать входящие данные, а другая - упорядочить данные для отображения. Ваш метод обновления должен переместить все завершенные буферизованные данные в раздел отображаемых данных.
Кроме того, вам, вероятно, потребуется установить таймер, когда пользователь не перемещает стол. Таймер должен вызвать метод обновления, если таблица не используется активно, а затем он должен принудительно выполнить обновление.
Если вы используете Core Data, вы можете использовать NSFetchedResultController
, и его методы-делегаты сообщат вам об изменении модели данных. Он должен возвращать правильную информацию о разделе и строке, обновленную в реальном времени. Управлять таким образом обновляющей таблицей довольно просто. Однако это не решит проблему ввода данных в модель так быстро, что модель будет меняться между вызовами методов. Вам все равно придется заморозить и / или замедлить модель.Однако таймер вам не понадобится.
Core Data - ваш лучший вариант, но даже в этом случае его будет сложно реализовать, потому что вы пытаетесь сделать что-то вопреки структуре пользовательского интерфейса, и поэтому API не поддерживает это легко.
Оглядываясь назад на этот ответ, я вижу, что я не упомянул [UITableView beginUpdates]
, который заморозит конфигурацию таблицы при добавлении или удалении строк. Он соединен с [UITableView endUpdates]
для включения изменений в пользовательский интерфейс.
Вызывать методы объектов GUI (например, UITableView) из фонового потока небезопасно.
Вам нужно сделать что-то вроде:
[ tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]