Почему ячейки моего табличного представления исчезают при перезагрузке с использованием reloadRowsAtIndexPaths? - PullRequest
19 голосов
/ 10 марта 2012

У меня есть пример проекта здесь:

http://dl.dropbox.com/u/7834263/ExpandingCells.zip

В этом проекте UITableView имеет пользовательский UITableViewCell.В каждой ячейке есть 3 UIViews, содержащие метку.

Цель состоит в том, чтобы развернуть ячейку при касании, а затем свернуть ее при повторном касании.Сама ячейка должна изменить свою высоту, чтобы выставить подпредставления.Вставка или удаление строк недопустимы.

Демонстрационный проект работает почти так, как ожидается.На самом деле в iOS 4.3 все работает отлично.Однако в iOS 5, когда строки разрушаются, предыдущие ячейки волшебным образом исчезают.

Чтобы заново создать проблему, запустите проект в симуляторе или устройстве с iOS 5 и коснитесь первой ячейки, чтобы развернуть ее.Затем снова нажмите на ячейку, чтобы свернуть ее.Наконец, коснитесь клетки прямо под ней.Предыдущий исчезает.

Продолжение постукивания по каждой ячейке в разделе приведет к исчезновению всех ячеек, в которых отсутствует весь раздел.

Я также попытался использовать reloadData вместотекущей настройки, но это разрушает анимацию и все равно выглядит как хак.reloadRowsAtIndexPaths должен работать, но вопрос в том, почему это не так?

Смотрите изображения того, что происходит ниже:

Появляется таблица:

table appears

Ячейка расширяется:

table expands

Ячейка разрушается:

table collapses

Ячейка исчезает (при нажатии на ячейку снизу):

cell disappears

Продолжайте повторять до тех пор, пока не исчезнет весь раздел:

section disappears

РЕДАКТИРОВАТЬ: Переопределение альфы - это взлом,но работает.Вот еще один «хак», который также исправляет это, но ПОЧЕМУ это исправляет?

JVViewController.m line 125:

if( previousIndexPath_ != nil )
{
    if( [previousIndexPath_ compare:indexPath] == NSOrderedSame ) currentCellSameAsPreviousCell = YES;

    JVCell *previousCell = (JVCell*)[self cellForIndexPath:previousIndexPath_];

    BOOL expanded = [previousCell expanded];
    if( expanded )
    {
        [previousCell setExpanded:NO];
        [indicesToReload addObject:[previousIndexPath_ copy]];
    }
    else if( currentCellSameAsPreviousCell )
    {
        [previousCell setExpanded:YES];
        [indicesToReload addObject:[previousIndexPath_ copy]];
    }

    //[indicesToReload addObject:[previousIndexPath_ copy]];
}

EDIT 2:

Внесены несколько незначительных изменений в демонстрационный проект, которые стоит проверить и просмотреть в методе JSViewController didSelectRowAtIndexPath.

Ответы [ 12 ]

21 голосов
/ 21 марта 2012

Ваша проблема в setExpanded: в JVCell.m вы напрямую редактируете фрейм целевой ячейки в этом методе.

- (void)setExpanded:(BOOL)expanded
{
    expanded_ = expanded;

    CGFloat newHeight = heightCollapsed_;
    if( expanded_ ) newHeight = heightExpanded_;

    CGRect frame = self.frame;
    frame.size.height = newHeight;
    self.frame = frame;
}

Обновите его до:

- (void)setExpanded:(BOOL)expanded
{
    expanded_ = expanded;
}

Затем удалите вызов к -reloadRowsAtIndexPaths:withRowAnimation: в строке 163 JVViewController.m, и он будет анимирован, как и ожидалось.

-reloadRowsAtIndexPaths:withRowAnimation: ожидает, что для указанных indexPaths будут возвращены разные ячейки. Поскольку вы настраиваете только размеры, -beginUpdates & -endUpdates достаточно, чтобы снова расположить ячейки табличного представления.

10 голосов
/ 21 марта 2012

Может быть, мне не хватает точки, но почему бы вам просто не использовать:

UITableViewRowAnimationNone

Я имею в виду вместо:

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationAutomatic];

использовать

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationNone];
8 голосов
/ 21 февраля 2014

Для анимации изменений высоты tableView просто вызовите.

[tableView beginUpdates];
[tableView endUpdates];

Не вызывайте reloadRowsAtIndexPaths:

См. Можете ли вы анимировать изменение высоты на UITableViewCell при выборе

5 голосов
/ 19 марта 2012

Затухающая ячейка - это предыдущая ячейка, размер которой не изменяется. В документации reloadRowsAtIndexPaths:withRowAnimation: указано:

Таблица анимирует эту новую ячейку так же, как она оживляет старый ряд.

Что происходит, если для непрозрачности установлено значение 1, а затем сразу устанавливается значение 0, и поэтому оно исчезает.

Если и предыдущая, и новая ячейка меняют размер, значит, он работает как задумано. Это происходит потому, что обновления начала / конца замечают изменения высоты и создают новые анимации в тех ячейках, которые перекрывают reloadRowsAtIndexPaths:withRowAnimation:.

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

Но вам не нужно reloadRowsAtIndexPaths:withRowAnimation: вообще. Просто измените расширенное состояние ячеек и начните / завершите обновления. Это будет обрабатывать всю анимацию для вас.

В качестве примечания я обнаружил, что выделение синего цвета немного раздражает, в JVCell установите selectedBackgroundView на то же изображение, что и backgroundView (или создайте новое изображение, которое имеет правильный вид выбранной ячейки).


EDIT:

Переместите оператор, добавив previousIndexPath_ к indicesToReload, в оператор if (в строке 132), чтобы он добавлялся только в том случае, если предыдущая ячейка была расширена и ее размер нужно изменить.

if( expanded ) {
    [previousCell setExpanded:NO];
    [indicesToReload addObject:[previousIndexPath_ copy]];
}

Это удаляет случай, когда предыдущая свернутая ячейка исчезнет.

Другой вариант - установить previousIndexPath_ на ноль, когда текущая ячейка свернута, и установить его только при расширении ячейки.

Это все равно что взломать. Выполнение обновлений reloadRows и start / end приводит к тому, что tableView перезагружает все дважды, но, похоже, для правильной анимации оба необходимы. Я полагаю, если таблица не слишком большая, это не будет проблемой производительности.

2 голосов
/ 17 марта 2012

Краткий, прагматичный ответ: изменение UITableViewRowAnimationAutomatic на UITableViewRowAnimationTop решает проблему.Нет больше исчезающих строк!(протестировано на iOS 5.1)

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

Еще несколько мыслей относительно анимации перезагрузки той же самой ячейки:

UITableViewRowAnimationAutomatic, кажется, разрешает UITableViewRowAnimationFade в некоторых случаях, когда вы видитеклетки исчезают и исчезают.Предполагается, что новая ячейка исчезнет, ​​а старая исчезнет.Но здесь старая и новая ячейки - это одно и то же. Так что же это может сработать?На уровне основной анимации, возможно ли затемнить изображение и одновременно его отобразить?Звучит сомнительно.В результате вы просто видите исчезновение.Это можно считать ошибкой Apple, поскольку ожидаемое поведение может заключаться в том, что если изменилось то же представление, свойство alpha не будет анимированным (поскольку оно не может одновременно анимировать как 0, так и 1), новместо этого анимируются только рамка, цвет и т. д.

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

В iOS 4.3Режим Automatic, возможно, был разрешен для чего-то другого, кроме Fade, поэтому там все работает (как вы пишете, они работают) - я не копался в этом.

Я не знаю, почему iOSпри этом выбирает режим Fade.Но один из случаев, когда делает , - это когда ваш код запрашивает перезагрузку ранее повернутой ячейки, которая свернута и отличается от текущей повернутой ячейки.Обратите внимание, что ранее подключенная ячейка всегда перезагружается, эта строка в вашем коде всегда называется:

[indicesToReload addObject:[previousIndexPath_ copy]];

Это объясняет сценарий магических исчезающих ячеек, который вы описали.

Кстати, beginUpdates / endUpdates кажутся мне взломом.Предполагается, что эта пара вызовов содержит анимации, и вы не добавляете анимации в дополнение к строкам, которые вы уже просили перезагрузить.Все, что он сделал в этом случае, магическим образом заставляет режим Automatic не выбирать Fade в некоторых случаях - но это только затеняет проблему.

Последнее замечание: я играл с режимом Topи обнаружил, что это также может вызвать проблемы.Например, при подключении следующего кода ячейки быстро исчезают:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
}

Не уверен, что здесь есть реальная проблема (похожая на ту, с которой исчезает вид из окна в одно и то же время), или, может быть,Apple ошибка.

1 голос
/ 12 июля 2013

Когда вы используете этот метод, вы должны быть уверены, что находитесь в главном потоке.Обновление UITableViewCell следующим образом должно помочь:

- (void) refreshTableViewCell:(NSNumber *)row
{
    if (![[NSThread currentThread] isMainThread])
    {
        [self performSelector:_cmd onThread:[NSThread mainThread]  withObject:row waitUntilDone:NO];
        return;
    }

    /*Refresh your cell here
     ...
     */

}
1 голос
/ 16 марта 2012

Я только что загрузил ваш проект и нашел этот раздел кода в didSelectRowAtIndexPath делегате, где используется reloadRowsAtIndexPaths.

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView beginUpdates];
[tableView endUpdates];

вместо вышеперечисленного, почему бы вам не попробовать это?

[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];

Причина, по которой я предлагаю это, заключается в том, что я считаю, что reloadRowsAtIndexPaths:... работает только тогда, когда заключены между вызовами:

- (void)beginUpdates;
- (void)endUpdates;

Помимо этого, поведение не определено и, как вы обнаружили, довольно ненадежно. Цитируем соответствующую часть " Руководство по программированию табличного представления для iPhone OS ":

Чтобы анимировать пакетную вставку и удаление строк и разделов, вызовите методы вставки и удаления в блоке анимации, определяемом последовательными вызовами beginUpdates и endUpdates. Если вы не вызываете методы вставки и удаления в этом блоке, индексы строк и разделов могут быть недействительными. beginUpdates ... блоки endUpdates не являются вложенными.

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

reloadSections: withRowAnimation: и reloadRowsAtIndexPaths: withRowAnimation: методы, которые были представлены в iPhone OS 3.0, относятся к методам, рассмотренным выше. Они позволяют запрашивать табличное представление для перезагрузки данных для определенных разделов и строк вместо загрузки всего видимого табличного представления путем вызова reloadData.

Может быть и другая веская причина, но позвольте мне немного обдумать это, поскольку у меня тоже есть ваш код, и я мог бы обойти его. Надеюсь, мы должны это выяснить ...

0 голосов
/ 20 августа 2016

У меня была похожая проблема, когда я хотел расширить ячейку, когда активирован переключатель для отображения, а также дополнительная метка и кнопка в ячейке, которая обычно скрыта, когда ячейка находится на высоте по умолчанию (44). Я пробовал разные версии reloadRowsAtPath безрезультатно. Наконец, я решил сделать это проще, добавив условие на heightForRowAtIndexPath, например, так:

    override func tableView(tableView: UITableView,heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if ( indexPath.row == 2){
        resetIndexPath.append(indexPath)
        if resetPassword.on {
            // whatever height you want to set the row to
            return 125
        }
    }
    return 44
}

И в любом коде, который вы хотите запустить расширение ячейки, просто вставьте tableview.reloadData (). В моем случае это было, когда был включен переключатель, указывающий на желание сбросить пароль.

        @IBAction func resetPasswordSwitch(sender: AnyObject) {

        tableView.reloadData()
        }

В этом подходе нет задержки, нет видимого способа увидеть, что таблица перезагружается и расширение продаж происходит постепенно, как и следовало ожидать. Надеюсь, это кому-нибудь поможет.

0 голосов
/ 12 ноября 2012

Эта проблема вызвана возвращением кэшированных ячеек в cellForRowAtIndexPath.Ожидается, что reloadRowsAtIndexPaths получит новые новые ячейки из cellForRowAtIndexPath.Если вы сделаете это, у вас все будет в порядке ... никаких обходных путей не требуется.

Из Apple doc: «При перезагрузке строки представление таблицы запрашивает у своего источника данных новую ячейку для этой строки».

0 голосов
/ 22 марта 2012

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

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL currentCellSameAsPreviousCell = NO;
NSMutableArray *indicesToReload = [NSMutableArray array];

if(previousIndexPath_ != nil)
{
    if( [previousIndexPath_ compare:indexPath] == NSOrderedSame ) currentCellSameAsPreviousCell = YES;

    JVCell *previousCell = (JVCell*)[self cellForIndexPath:previousIndexPath_];

    BOOL expanded = [previousCell expanded];
    if(expanded) 
    {
     [previousCell setExpanded:NO];
    }
    else if  (currentCellSameAsPreviousCell)
    {
        [previousCell setExpanded:YES];
    }
    [indicesToReload addObject:[previousIndexPath_ copy]];

    if (expanded)
        previousIndexPath_ = nil;
    else
        previousIndexPath_ = [indexPath copy];         
}

if(currentCellSameAsPreviousCell == NO)
{
    JVCell *currentCell = (JVCell*)[self cellForIndexPath:indexPath];

    BOOL expanded = [currentCell expanded];
    if(expanded) 
    {
        [currentCell setExpanded:NO];
        previousIndexPath_ = nil;
    }

    else
    {
        [currentCell setExpanded:YES];
        previousIndexPath_ = [indexPath copy];
    }

    // moving this line to inside the if statement blocks above instead of outside the loop works, but why?
    [indicesToReload addObject:[indexPath copy]];


}

// commenting out this line makes the animations work, but the table view background is visible between the cells

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationAutomatic];

// using reloadData completely ruins the animations
[tableView beginUpdates];

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