Хранение ячейки-заполнителя в UITableView - PullRequest
4 голосов
/ 05 января 2011

У меня есть UITableView, который я никогда не хочу опускаться ниже 1 ячейки: это считывание каталога, и если в каталоге нет файлов, он имеет одну ячейку с надписью «Нет файлов».(В режиме редактирования есть бонусная ячейка для создания файла, поэтому режим редактирования никогда не опускается ниже двух ячеек.)

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

*** Terminating app due to uncaught exception 
'NSInternalInconsistencyException', reason: 'Invalid update: 
invalid number of sections.  The number of sections contained 
in the table view after the update (2) must be equal to 
the number of sections contained in the table view before 
the update (2), plus or minus the number of sections inserted 
or deleted (1 inserted, 0 deleted).'

Это происходит, поскольку я предварительно добавляю заполнитель "Нет файлов" перед удалением последнего файла.Подобный сбой происходит, если я удаляю последнюю ячейку файла перед добавлением заполнителя.В любом случае, число ячеек не синхронизируется с возвращением numberOfRowsInSection, и это вызывает сбой.

Конечно, для этой ситуации есть шаблон проектирования.Подскажите мне?

Ответы [ 3 ]

3 голосов
/ 08 мая 2013

У меня есть другое решение для этого (кажется, есть много способов сделать это).Я обнаружил, что другие доступные решения не удовлетворяли моим потребностям, поэтому я придумал это.

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

Звучит отлично?Хорошо!Давайте посмотрим на суть:

Реальная проблема с большинством решений для ячеек-заполнителей заключается в том, что они обычно терпят неудачу, когда вы даете разрешение на редактирование - удаление и вставку - на вашей таблице.Это, или вы должны начать возиться с кодом, который обрабатывает редактирование, что может сделать все более запутанным.Проблема здесь обычно возникает при возврате несовместимых значений в методе numberOfRowsInSection.В табличном представлении обычно возникает проблема, скажем, удаления ячейки в таблице, в которой осталась одна ячейка, и еще одной ячейки после удаления (или наоборот со вставкой).

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

Несмотря на длительную запись, реализовать это на самом деле очень просто.Давайте начнем:

1.Настройте прототипы ячеек-заполнителей: Это довольно просто.Установите ячейки прототипа в вашей раскадровке для ваших заполнителей.В моем случае я использую два: один для отображения «Загрузка ...», когда таблица получает данные с сервера, а другой для отображения «Нажмите + выше, чтобы добавить элемент», когда в действительности ничего неттаблица.

Настройте ваши ячейки визуально, как вам нравится (я просто использовал стиль ячейки Subtitle и поместил текст моего заполнителя в метку субтитра. Не забудьте удалить текст другой метки, если вы это сделаете).Обязательно назначьте идентификатор повторного использования и установите стиль выбора на «Нет».

2.Настройте метод делегата numberOfRowsInSection: Теперь этот метод будет выполнять две основные вещи: во-первых, нужно вернуть количество строк в источнике данных (плюс одну для нашего заполнителя), а во-вторых, показать / скрыть текст нашего заполнителя.как необходимо.Это хорошее место для инициирования этого, так как этот метод вызывается каждый раз, когда ячейка удаляется или вставляется (фактически дважды: один раз перед редактированием и один раз после).Чтобы избежать запуска анимации при каждом вызове метода, мы будем использовать BOOL placeholderIsHidden, чтобы отслеживать текущий статус нашего заполнителя.Мы также выполним переключение после небольшой задержки, чтобы начать анимацию редактирования ячейки.Добавьте этот код в свой класс:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int count = [self.dataSource count];

    // Hide/Show placeholder cell
    if (count == 0) {   // Placeholder should be shown
        if (self.placeholderIsHidden) {
            [self performSelector:@selector(animatePlaceholderCellChangeForIndexPath:) withObject:[NSIndexPath indexPathForRow:count inSection:0] afterDelay:0.1];
        }
    }
    else {   // Placeholder should be hidden
        if (!self.placeholderIsHidden) {
            [self performSelector:@selector(animatePlaceholderCellChangeForIndexPath:) withObject:[NSIndexPath indexPathForRow:count inSection:0] afterDelay:0.1];
        }
    }

    return count + 1;
}

Это хорошо!Теперь давайте добавим наш метод animatePlaceholderCellChange:

- (void)animatePlaceholderCellChangeForIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.8];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationBeginsFromCurrentState:YES];

    if (indexPath.row == 0) {
        cell.detailTextLabel.hidden = NO;
        self.placeholderIsHidden = NO;
    }
    else {
        cell.detailTextLabel.hidden = YES;
        self.placeholderIsHidden = YES;
    }

    [UIView commitAnimations];
}

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

Во-вторых, нам нужно настроить наш cellForRowAtIndexPath метод для возврата правильных ячеек.

3.Настройка cellForRowAtIndexPath: Это довольно просто.Поместите этот код в ваш метод, после объявления идентификатора ячейки прототипа и до выполнения обычной настройки ячейки:

// Add a placeholder cell while waiting on table data
int nodeCount = [self.dataSource count];

if (indexPath.row == nodeCount) {
     // Place the appropriate type of placeholder cell in the first row
     if (self.isLoading) {
         return [tableView dequeueReusableCellWithIdentifier:LoadingCellIdentifier];
     }
     else {
         return [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
     }
 }

Наконец, давайте настроим наши свойства BOOL для отслеживания загрузки данных изаполнитель скрыт или нет.

4.Настройка isLoading и placeholderIsHidden:

Сначала добавьте два объявления в файл .h вашего класса:

@property (assign, nonatomic) BOOL isLoading;
@property (assign, nonatomic) BOOL placeholderIsHidden;

Теперь установите их начальные значения в вашем viewDidLoad метод:

self.isLoading = YES;
self.placeholderIsHidden = NO;

Вот и все для свойства placeholderIsHidden! Что касается свойства isLoading, вам нужно установить его на YES везде, где код начинает загружать данные с вашего сервера, и NO, где эта операция завершается. В моем случае это было довольно просто, поскольку я использую операцию с обратными вызовами для завершения операции загрузки.

Вот и все! Запустите свой код и увидите аккуратную, плавно анимированную и безопасную для редактирования ячейку-заполнитель!

Надеюсь, это кому-нибудь поможет!

РЕДАКТИРОВАТЬ: Еще одна важная вещь! Важно отметить, что есть одна больше вещей, которые вы должны делать с вашим кодом, чтобы избежать неприятных непредвиденных сбоев: через ваш код и везде, где вы получаете доступ к элементам из вашего источника данных (обычно используя objectAtIndex:), убедитесь, что вы никогда не пытаетесь извлечь элемент, который не существует.

Это может быть проблемой в нескольких редких случаях, когда, например, у вас есть код, который обращается к элементам в источнике данных для всех видимых строк на экране (поскольку заполнитель может быть видимым, вы можете иметь indexPath, который не связан ни с одним элементом в вашем источнике данных) . (В моем случае это был конкретный фрагмент кода, который запускал загрузку изображений во всех видимых строках после прокрутки представления таблицы). Решить это довольно просто: везде, где вы получаете доступ к элементам из вашего источника данных, поместите оператор if вокруг кода следующим образом:

int count = [self.dataSource count];

if (indexPath.row != count) {

   // Code that accesses the element
   // ie:
   MyDataItem *dataItem = [self.dataSource objectAtIndex:indexPath.row];
}

Удачи!

3 голосов
/ 05 января 2011

Сделайте что-нибудь в соответствии с приведенным ниже фрагментом кода:

  • сначала удалите данные для строки из массива
  • если элементы массива не упали до нуля, тогда удалить строку из таблицы
  • если элементы массива упали до нуля, то перезагрузите таблицу - обратите внимание: ваш код должен теперь предоставить 1 для числа строк и настроить ячейку для строки 0, чтобы она отображала «Нет файлов» при вызове методов делегата таблицы.
 
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // First delete the row from the data source
        [self deleteTableData: indexPath.row];  // method that deletes data from 
        if ([self.tableDataArray count] != 0) {
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        } else {
            [self.tableView reloadData];
        }

    }   
}
1 голос
/ 08 октября 2011

Самый простой способ сделать это - 1001 * абсолют - это иметь два UITableView

например

UITableView * mainTable; 
UITableView * placeholderTable;

Затем вы выполняете такие вещи в своем делегате, как

tableView:(UITableView*)tableview cellForRowAtIndexPath:(NSIndexPath*)indexPath {
  if (tableview == placeholderTable){
    // code for rendering placeholder cell
  }
  else {
    // code for rendering actual table content
  }
}

Всякий раз, когда вы вставляете / удаляете сущности или инициализируете представление, проверяете пустое условие и скрываете / отображаете ваши таблицы:

// something changed the number of cells
    if (tableIsEmpty){
      mainTable.hidden = YES;
      placeholderTable.hidden = NO;
    }
    else {
      mainTable.hidden = NO;
      placeholderTable.hidden = YES;
    }

Выполнение этого таким образом спасет вас от тонны горя;особенно при работе с Core Data NSFetchedResultsController и анимацией табличного представления.

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