Проблема с производительностью: удаление большого количества ячеек из UITableView
У меня есть табличное представление с более чем 1000 ячеек (я отображаю около 10 на экране). После действия пользователя "фильтровать" я хочу удалить около 80% из них, затем после второго фильтра еще 80%. При работе с 100 ячейками это примерно мгновенно, но с 1000 требуется 0,5 секунды для завершения метода [tableView endUpdates], и это полностью разрушает взаимодействие с пользователем.
Я создаю массив путей индекса следующим образом:
for (int i=1; i<[appDelegate.myArray count]; i++) { //more than 1000 objects in myArray
if (![[appDelegate.myArray objectAtIndex:i] myTest]) { //myTest returns a BOOL
[appDelegate.rowsToDelete addObject:[NSIndexPath xxxxx]]; //the index path of the object that just failed the test and must be removed];
}
}
[self.myTableViewController.tableView deleteRowsAtIndexPaths:appDelegate.rowsToDelete withRowAnimation:UITableViewRowAnimationRight];
Я испробовал 2 решения, одно из которых является многопоточным, чтобы мой пользователь мог видеть, как другие части пользовательского интерфейса перемещаются в процессе обновления tableView. Но если он запускает другой уровень фильтрации, это часто приводит к долгому ожиданию, если не к краху.
Я также попытался удалить первые несколько ячеек (те, что на экране), а затем просто удалить другие объекты, которые не прошли тест, из источника данных, не удаляя их соответствующие ячейки из tableView, поскольку они находятся вне поля зрения. Это дает хороший быстрый результат, но у меня возникают проблемы с количеством строк, таких как:
"Неверное обновление: недопустимое количество строк в разделе 0. Число строк, содержащихся в существующем разделе после обновления (94), должно быть равно числу строк, содержащихся в этом разделе до обновления (927), плюс или минус количество строк, вставленных или удаленных из этого раздела (0 вставлено, 215 удалено).'
У кого-нибудь была такая проблема с производительностью? способы решить это? Ура, CD
3 ответа
Попробуйте кэшировать свойства и использовать быстрое перечисление для вашего цикла:
//int i = 0;
NSMutableArray *rowsToDelete = appDelegate.rowsToDelete;
for(id current in appDelegate.myArray) {
if([current myTest]) [rowsToDelete addObject:[NSIndexPath ...]];
//++i;
}
[self.myTableViewController.tableView deleteRowsAtIndexPaths:rowsToDelete withRowAnimation:UITableViewRowAnimationRight];
Если вы используете индекс элемента для создания NSIndexPath, раскомментируйте первую и пятую строки. Также я заметил, что в вашем коде вы начинаете с i=1. Поскольку NSArrays начинаются с 0, это никогда не удалит первую ячейку. Если вы действительно этого хотите, измените i = 0 на i=1.
Я согласен с @futureelite7, что вам следует перезагрузить источник данных, а не манипулировать представлением. Я предложил решение аналогичного вопроса здесь.
Основная идея заключается в вызове reloadSections:withRowAnimation: и в ваших методах UITableViewDataSource включите сегментированный элемент управления selectedSegmentIndex.
Если предположить, что ваши данные плоские (только один раздел), они будут выглядеть примерно так:
- (IBAction)segmentSwitch:(id)sender
{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
return [self.allRows count];
case 1:
return [self.onlySomeRows count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id data;
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
data = [self.allRows objectAtIndex:[indexPath row]];
break;
case 1:
data = [self.onlySomeRows objectAtIndex:[indexPath row]];
break;
}
//TODO: use data to populate and return a UITableViewCell...
}
Возможно, вы просто захотите выполнить фильтрацию и перезагрузить все табличное представление, используя новое количество строк. Нет смысла делать все эти анимации удаления, если они все равно оказываются вне поля зрения. Или просто выяснить, какая часть видима, затем загрузить и удалить только те строки, которые в данный момент видны пользователю?