У меня есть другое решение для этого (кажется, есть много способов сделать это).Я обнаружил, что другие доступные решения не удовлетворяли моим потребностям, поэтому я придумал это.
Этот метод очень прост и хорош для тех из вас, кто хочет использовать два (или больше)ячейки-заполнители, не хотите возиться с вашей моделью данных или несколькими представлениями таблиц, и , в частности, - необходимо разрешить удаление и вставку ячеек в вашу таблицу.Это также обеспечивает очень хороший переход между входом и выходом для отображения и скрытия заполнителя.
Звучит отлично?Хорошо!Давайте посмотрим на суть:
Реальная проблема с большинством решений для ячеек-заполнителей заключается в том, что они обычно терпят неудачу, когда вы даете разрешение на редактирование - удаление и вставку - на вашей таблице.Это, или вы должны начать возиться с кодом, который обрабатывает редактирование, что может сделать все более запутанным.Проблема здесь обычно возникает при возврате несовместимых значений в методе 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];
}
Удачи!