Рассмотрим очень простой UITableView с одним из двух состояний.
Первое состояние:
- Один (общий) нижний колонтитул таблицы
- Один раздел, содержащий две строки, заголовок раздела и нижний колонтитул раздела
Второе состояние:
- Нет нижнего колонтитула таблицы
- Один раздел, содержащий четыре строки и без верхнего / нижнего колонтитула раздела
В обоих случаях каждая строка по сути является одним из четырех возможных UITableViewCell объектов, каждый из которых содержит свой собственный UITextField . Мы даже не беспокоимся о повторном использовании или кэшировании, поскольку в данном случае мы имеем дело только с четырьмя известными ячейками. Они были созданы в сопровождающем XIB, поэтому у нас уже есть все они подключены и готовы к работе.
Теперь рассмотрим, что мы хотим переключаться между двумя состояниями.
Звучит достаточно просто. Давайте предположим, что элемент кнопки правой панели контроллера представления обеспечивает поддержку переключения. Мы также будем отслеживать текущее состояние с помощью ивара и перечисления.
Чтобы быть явным в течение секунды, вот как можно перейти из состояния 1 в 2. (Предположим, что мы также обрабатываем заголовок элемента панели кнопок.) Короче говоря, мы хотим очистить представление нижнего колонтитула нашей таблицы, а затем вставить третий и четвертый ряды. Мы пакетируем это внутри блока обновления примерно так:
// Brute forced references to the third and fourth rows in section 0
NSUInteger row02[] = {0, 2};
NSUInteger row03[] = {0, 3};
[self.tableView beginUpdates];
state = tableStateTwo; // 'internal' iVar, not a property
self.tableView.tableFooterView = nil;
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Для обратного мы хотим переназначить представление нижнего колонтитула таблицы (которое, как и ячейки, находится в готовом и ожидающем XIB), и удалить последние две строки:
// Use row02 and row03 from earlier snippet
[self.tableView beginUpdates];
state = tableStateOne;
self.tableView.tableFooterView = theTableFooterView;
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Теперь, когда таблица запрашивает строки, она очень обрезная и сухая. Первые две ячейки одинаковы в обоих случаях. Только последние два появляются / исчезают в зависимости от состояния. С состоянием ivar обращаются, когда табличное представление запрашивает такие вещи, как количество строк в разделе, высоту верхнего / нижнего колонтитула в разделе или представление верхнего / нижнего колонтитула в разделе.
В этом последнем эпизоде также возникают проблемы.
Используя приведенную выше логику, верхний / нижний колонтитул раздела 0 не исчезает. В частности, нижний колонтитул остается ниже вставленных строк, но заголовок теперь перекрывает самый верхний ряд. Если мы вернемся к первому состоянию, нижний колонтитул раздела будет удален, но заголовок раздела останется.
Как насчет использования [self.tableView reloadData]
тогда? Конечно, почему бы и нет. Мы стараемся не использовать его внутри блока обновления, согласно совету Apple, и просто добавляем его после endUpdates
.
На этот раз хорошие новости! Верхний / нижний колонтитул раздела 0 исчезает. :)
Однако ...
Переключение на одно состояние приводит к самым изысканным беспорядкам! Заголовок раздела 0 возвращается только для того, чтобы снова наложить первый ряд (вместо того, чтобы появляться над ним). Нижний колонтитул раздела 0 помещается ниже последней строки, но общий нижний колонтитул таблицы - теперь восстановленный - накладывается на нижний колонтитул раздела. Waaaaaah ... что теперь?
Просто чтобы быть уверенным, давайте вернемся к состоянию два снова. Да, это выглядит хорошо. Возвращаясь, чтобы заявить один? Yecccch.
Я также пытался посыпать несколько других трюков, например, используя reloadSections:withRowAnimation:
, но это только усугубляет ситуацию.
NSRange range = {0, 1};
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
...
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
Показательный пример: если мы вызываем reloadSections...
непосредственно перед концом блока обновления, переход в состояние два скрывает первые две строки, даже если остается место, которое они в противном случае занимали бы. При возврате к первому состоянию возвращается верхний / нижний колонтитул секции 0 к нормальному состоянию, но первые две строки остаются невидимыми.
Случай два: перемещение reloadSections...
сразу после блока обновления, но до reloadData
приводит к тому, что все строки становятся невидимыми! (Я называю строку невидимой, поскольку во время трассировки tableView:cellForRowAtIndexPath:
означает , возвращая истинные объекты ячеек для этих строк.)
Случай третий: перемещение reloadSections...
после tableView:cellForRowAtIndexPath:
делает нас немного ближе, но верхний / нижний колонтитул раздела 0 никогда не возвращается при переходе в исходное состояние.
Хм. Возможно, это ложная ошибка, использующая и reloadSections...
, и reloadData
, основываясь на том, что я вижу во время трассировки, что приводит нас к:
Случай четвертый: прямая замена reloadData
на reloadSections...
. Все клетки в состоянии два исчезают. Все ячейки в состоянии один также остаются отсутствующими (хотя пространство остается).
Так много для этой теории. :)
Трассировка кода, объектов ячеек и представлений, а также высоты секций - все, где они должны быть в подходящее время. Они просто не делают разумно. ( Обновление: Высота просмотра НЕ одинакова, но я тоже не менял их! Смотрите мой опубликованный ответ для получения дополнительной информации.)
Итак, как взломать этот случай? Подсказки приветствуются / приветствуются!