UITableView: сопоставление вставок / удалений строк, заголовков и нижнего колонтитула - PullRequest
1 голос
/ 18 мая 2010

Рассмотрим очень простой 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.... Все клетки в состоянии два исчезают. Все ячейки в состоянии один также остаются отсутствующими (хотя пространство остается).

Так много для этой теории. :)

Трассировка кода, объектов ячеек и представлений, а также высоты секций - все, где они должны быть в подходящее время. Они просто не делают разумно. ( Обновление: Высота просмотра НЕ одинакова, но я тоже не менял их! Смотрите мой опубликованный ответ для получения дополнительной информации.)

Итак, как взломать этот случай? Подсказки приветствуются / приветствуются!

Ответы [ 2 ]

1 голос
/ 08 января 2011

У меня была такая же проблема сегодня.

Через некоторое время у меня возникла мысль, что в том, как все нарисовано, есть ошибка. В появлении метода ячеек табличного представления я удалил условие условия, как это, все работало:

    //if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];    
    //}

Я более подробно рассмотрю проблему, поскольку код больше не оптимизируется. Но пока это работает.

0 голосов
/ 18 мая 2010

Я помещаю это в раздел ответов, потому что это помогает (частично?) Объяснить, что я вижу. Это просто не объясняет почему пока. :)

Когда я впервые назначаю представление заголовку раздела таблицы (в ответ на tableView:viewForHeaderInSection:), он устанавливается так же, как я определил его в XIB:

<UIView: 0x376620; frame = (0 0; 320 50); autoresize = RM+BM;
 layer = <CALayer: 0x376720>>

Через некоторое время после того, как мы изменили ячейки и начали реагировать на tableView:viewForHeaderInSection: с нулем для секции 0, указанное представление получает некоторую CABasicAnimation любовь (в конце концов, таблица собирается анимировать) ... а рамка просмотра и параметры авторазмера меняются!

<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W; 
 animations = { position=<CABasicAnimation: 0x360b30>;
 bounds=<CABasicAnimation: 0x360a30>; }; layer = <CALayer: 0x376720>>

Если мы затем переключим BACK в первое состояние и вернем тот же самый вид, что и заголовок раздела, мы увидим немного мусора в назначенное время:

<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W; 
 layer = <CALayer: 0x376720>>

Да. Рамка и авторазмер не сбрасываются!

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

Моя реакция коленного рефлекса: «Если вы собираетесь возиться с моим UIView, пожалуйста, верните вещи так, как вы их нашли!» На данный момент я не знаю, реалистично ли это, но это первое, что приходит на ум.

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

Опять же, эта настройка кадра / авторазмера, по-видимому, не представляет проблемы для общего представления верхнего / нижнего колонтитула таблицы, даже если они удалены и позже добавлены заново. Просто представления верхнего / нижнего колонтитула раздела. (Подождите, поправка: я не могу сказать, влияет ли это на представление нижнего колонтитула таблицы, потому что под ним нет ничего, с чем можно столкнуться.)

На данный момент я включил другой вид в каждый вид верхнего / нижнего колонтитула. Это представление идентично по форме родительскому представлению, но оно не изменяется во время анимации. Тогда нужно просто вызвать что-то похожее на это для каждого представления верхнего / нижнего колонтитула, на которое влияет изменение состояния:

- (void)fixViewAnimationCruft:(UIView *)theView {
  UIView *subview = [[theView subviews] objectAtIndex:0];
  theView.frame = subview.frame;
  theView.autoresizingMask = subview.autoresizingMask;
}

(Не самое оригинальное имя метода, но оно пока подойдет.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...