Подтвердить что ты не робот

Почему ячейки таблицы просмотра исчезают при перезагрузке с помощью reloadRowsAtIndexPaths?

Здесь у меня есть пример с образцом:

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

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

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

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

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

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

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

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

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

table appears

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

table expands

Клетка разрушается:

table collapses

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

cell disappears

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

section disappears

EDIT: Переопределение альфы - это взлом, но работает. Вот еще один "взлом", который также исправляет его, но ПОЧЕМУ он его исправляет?

JVViewController.m строка 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]];
}

ИЗМЕНИТЬ 2:

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

4b9b3361

Ответ 1

Ваша проблема в 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, достаточно, чтобы снова отобразить ячейки таблицы.

Ответ 2

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

UITableViewRowAnimationNone

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

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationAutomatic];

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

[tableView reloadRowsAtIndexPaths:indicesToReload withRowAnimation:UITableViewRowAnimationNone];

Ответ 4

Ячейка, которая исчезает, является предыдущей ячейкой, которая не меняет размер. Как указано в документации 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_ на nil, когда текущая ячейка будет свернута и только установить ее, когда ячейка будет расширяться.

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

Ответ 5

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

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

Еще несколько соображений относительно оживления перезагрузки одной и той же ячейки:

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

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

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

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

[indicesToReload addObject:[previousIndexPath_ copy]];

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

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

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

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

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

Ответ 6

Я только что загрузил ваш проект и нашел этот раздел кода в делегате 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;

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

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

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

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

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

Ответ 7

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

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

    /*Refresh your cell here
     ...
     */

}

Ответ 8

@Javy, я заметил странное поведение во время тестирования вашего приложения.

При работе на симуляторе iPhone 5.0 предыдущая переменнаяIndexpth_ имеет

class NSArray (похоже.) здесь вывод отладчика

 (lldb) po previousIndexPath_
    (NSIndexPath *) $5 = 0x06a67bf0 <__NSArrayI 0x6a67bf0>(

    <JVSectionData: 0x6a61230>,

    <JVSectionData: 0x6a64920>,

    <JVSectionData: 0x6a66260>
    )

    (lldb) po [previousIndexPath_ class]

    (id) $7 = 0x0145cb64 __NSArrayI

Тогда как в симуляторе iPhone 4.3 он имеет тип NSIndexPath.

lldb) po [previousIndexPath_ class]

(id) $5 = 0x009605c8 NSIndexPath

(lldb) po previousIndexPath_

(NSIndexPath *) $6 = 0x04b5a570 <NSIndexPath 0x4b5a570> 2 indexes [0, 0]

Вам известна эта проблема? Не уверен, поможет ли это, но подумал дать вам знать.

Ответ 9

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

- (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];
}

Ответ 10

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

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

Ответ 11

У меня была аналогичная проблема, когда я хотел развернуть ячейку, когда переключатель активирован, чтобы отобразить, и дополнительную метку и кнопку в ячейке, которая обычно скрыта, когда ячейка имеет высоту по умолчанию (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(). В моем случае это было, когда переключатель был включен, чтобы указать желание reset пароля.

        @IBAction func resetPasswordSwitch(sender: AnyObject) {

        tableView.reloadData()
        }

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