Как создать панель инструментов между строками UITableView

#iphone #objective-c #user-interface #uitableview

#iPhone #objective-c #пользовательский интерфейс #uitableview

Вопрос:

Мне интересно, как tweetbot выполняет следующее:

Введите описание изображения здесь

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

Логика, я думаю, проста, вам просто нужно добавить вложенный просмотр в ячейку UITableViewCell при нажатии и сдвинуть остальное содержимое вверх, но как вы на самом деле отклоняете его, когда я нажимаю другую строку?

Ответ №1:

Лучший способ сделать это — добавить фиктивную ячейку под ячейкой, к которой было применено нажатие.

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

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    //if user tapped the same row twice let's start getting rid of the control cell
    if([indexPath isEqual:self.tappedIndexPath]){
        [tableView deselectRowAtIndexPath:indexPath animated:NO];
    }

    //update the indexpath if needed... I explain this below 
    indexPath = [self modelIndexPathforIndexPath:indexPath];

    //pointer to delete the control cell
    NSIndexPath *indexPathToDelete = self.controlRowIndexPath;

    //if in fact I tapped the same row twice lets clear our tapping trackers 
    if([indexPath isEqual:self.tappedIndexPath]){
        self.tappedIndexPath = nil;
        self.controlRowIndexPath = nil;
    }
    //otherwise let's update them appropriately 
    else{
        self.tappedIndexPath = indexPath; //the row the user just tapped. 
        //Now I set the location of where I need to add the dummy cell 
        self.controlRowIndexPath = [NSIndexPath indexPathForRow:indexPath.row   1   inSection:indexPath.section];
    }

    //all logic is done, lets start updating the table
    [tableView beginUpdates];

    //lets delete the control cell, either the user tapped the same row twice or tapped another row
    if(indexPathToDelete){
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] 
                          withRowAnimation:UITableViewRowAnimationNone];
    }
    //lets add the new control cell in the right place 
    if(self.controlRowIndexPath){
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath] 
                          withRowAnimation:UITableViewRowAnimationNone];
    }

    //and we are done... 
    [tableView endUpdates];  
} 
  

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

 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if(self.controlRowIndexPath){
        return modelArray.count   1;
    }
    return self.modelArray.count;
}
  

Кроме того, верните соответствующую высоту для вашей ячейки управления.

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath   *)indexPath 
{
    if([indexPath isEqual:self.controlRowIndexPath]){
        return 45; //height for control cell
    }
    return 70; //height for every other cell 
}
  

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

 - (NSIndexPath *)modelIndexPathforIndexPath:(NSIndexPath *)indexPath
{
    int whereIsTheControlRow = self.controlRowIndexPath.row;
    if(self.controlRowIndexPath != nil amp;amp; indexPath.row > whereIsTheControlRow)
        return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0]; 
    return indexPath;
}
  

Я надеюсь, что это поможет.

Комментарии:

1. Это правильно…. Я знаю, что большинство не захотят добавлять 1 к своему количеству, поскольку это просто добавит эту текущую ячейку ниже, и это выглядит глупо. Но если у вас пользовательская ячейка, вы можете использовать метод setSelected: (BOOL)selected animated:(BOO)animated, чтобы добавить красивое изображение и кнопки, как это делает tweetbot… Надеюсь, я прояснил несколько вещей

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

3. В основном: if(self.controlRowIndexPath amp;amp; [indexPath isEqual:self.controlRowIndexPath]) > верните ячейку для меню управления. Извините, нет исходного кода. Я уделил пристальное внимание сеансу WWDC и разобрался с остальным методом проб / ошибок.

4. В modelIndexPathforIndexPath вам необходимо заменить inSection: 0 на inSection:indexPath.section, если только у вас нет только одного раздела или это применимо только к первому разделу, но даже тогда возможны проблемы с обслуживанием, если раздел будет добавлен позже.

Ответ №2:

В tableView:didSelectRowAtIndexPath: вы удаляете представление инструмента из последней выбранной ячейки. Если такого представления нет, вы создаете новое. Затем вы добавляете это представление во вновь выбранную ячейку. Сохраните indexPath выбранной строки.

В tableView:heightForRowAtIndexPath: вы проверяете, совпадает ли indexPath с сохраненным indexPath. Если они равны, вы возвращаете высоту, которая равна высоте обоих представлений. Если значение не равно, просто верните высоту «реальной ячейки».

Поместите все ваши вызовы в didSelectRowAtIndexPath между [tableView beginUpdates] и [tableView endUpdates] , чтобы получить анимацию для изменения высоты.

Комментарии:

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

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

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

4. У меня получилось .. но анимация какая-то странная, поскольку анимируется текст внутри ячейки, а не панель инструментов .. в любом случае, чтобы анимировать панель инструментов при отображении?

5. какое это имеет отношение к панели? если у вас есть это приложение tweetbot, анимация довольно хорошая .. текст в ячейке остается статичным, а панель инструментов появляется и опускается вниз

Ответ №3:

код rjgonzo работает нормально, за исключением случая, когда у вас есть только 1 строка в tableview. Когда есть только 1 строка (и 1 объект в tableview datamodel), вы получите исключение NSRange при вызове insertRowsatIndexPath(ов). Чтобы исправить это, я проверил, имеет ли datamodel только 1 объект, и если это так, то я добавляю строку в текущий indexpath (вместо controlindexpath), в результате чего строка логически добавляется над первой строкой, а затем я вызываю moveRowAtIndexPath для обмена строками после вызова [self.TableView endUpdates]. Анимация отображается, как и ожидалось, с управляющей строкой, которая, по-видимому, скользит вниз с 1-й строки.

 if(self.controlRowIndexPath){

//Check to see if the datamodel only has 1 object

 if([self.objects count]==1){
//If so then insert the row above the row
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                              withRowAnimation:UITableViewRowAnimationNone];

    }
    else
    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
                          withRowAnimation:UITableViewRowAnimationNone];

 }

[self.tableView endUpdates];

//if the row was inserted above the 1st row then switch the rows

 if([self.objects count]==1)
 [self.tableView moveRowAtIndexPath:self.controlRowIndexPath toIndexPath:indexPath];
  

Комментарии:

1. Это было не только не нужно, но фактически нарушало желаемое поведение. У меня была похожая проблема «только один элемент, NSRange», но после попытки выполнения этого кода я понял, что проблема заключалась в том, что я использовал indexPath в том месте, где я должен был использовать self.tappedIndexPath в методе cellForIndexPath.

Ответ №4:

Я бы не стал добавлять вложенный просмотр в UITableViewCell , я бы добавил еще одну строку в UITableView . Таким образом, UITableView позаботится об анимации. (И я не думаю, что это возможно для анимированных UITableViewCell изменений высоты …)

Используйте просто

 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
  

добавьте строку. И

 - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
  

чтобы удалить ее.

Комментарии:

1. И вам также необходимо изменить методы вашего источника данных, чтобы вернуть вашу новую строку!

2. И распознать, как обрабатывать прикосновение к остальным ячейкам, пока отображается эта ячейка. 😉

3. insertRowsAtIndexPath просто указывает, куда она вставлена, но не отвечает на то, что вставлено.. итак, как мне указать, что вставляется?

4. Вам нужно изменить методы ваших источников данных. т. Е. Добавить одну строку в ‘numberOfSectionsInTableView:’ и вернуть новую строку в нужное место в ‘TableView:cellForRowAtIndexPath:’. Посмотрите на developer.apple.com/library/ios/#documentation/UserExperience / … для документации.

Ответ №5:

@rjgonzo это отлично работает, но есть небольшая проблема в том, как вы сохраняете indexPathToDelete. Поскольку это всего лишь еще один указатель на self.controlRowIndexPath, как только вы очистите или переназначите self.controlRowIndexPath, indexPathToDelete будет не тем, что вы хотели, а TableView удалитЬowsAtIndexPaths:с помощью withRowAnimation: call, вы получите сбой SIGBART.

итак, вместо

     //pointer to delete the control cell
    NSIndexPath *indexPathToDelete = self.controlRowIndexPath;
  

и

     //lets delete the control cell, either the user tapped the same row twice or tapped another row
    if(indexPathToDelete){
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] 
                      withRowAnimation:UITableViewRowAnimationNone];
    }
  

следующий код должен работать нормально:

     //pointer to delete the control cell
NSIndexPath *indexPathToDelete = [NSIndexPath indexPathForRow:self.control_row_index_path.row inSection:self.control_row_index_path.section];
    ...
    ...
    //lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete.row != 0){
    NSLog(@"row to delete %d", indexPathToDelete.row);
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete] withRowAnimation:UITableViewRowAnimationNone];
}
  

Ответ №6:

Вот что я делаю, чтобы анимация была чистой.

В дополнение к стратегии, которую предлагает пользователь fluchtpunkt (то есть добавление вложенного представления в ячейку, обновление высоты ячейки с помощью heightForRowAtIndexPath, beginUpdates и endUpdates), я нахожу следующие меры полезными для анимации:

  1. Ячейки tableviewcells имеют фоновое изображение. В противном случае добавленный вложенный вид / панель инструментов будет виден через ячейку непосредственно перед тем, как tableview анимирует изменение высоты.
  2. Ячейка tableviewcell находится «позади» представления, которое является ячейкой под ним, в противном случае снова подвидение / панель инструментов будут отображаться слишком рано. Я использую [tableview отправляет subviewtoback:cell]; и он позаботится об этом.

Для меня это чисто, но не совсем так, как Tweetbot. Интересно, что анимация Tweetbot, похоже, опускает панель инструментов вниз, когда анимируется нижняя часть ячейки. Похоже, что должна выполняться какая-то дополнительная анимация, или моя теория заговора заключается в том, что на самом деле это добавление вложенного представления в верхнюю часть ячейки под выбранной ячейкой, а затем выполнение удлинения и анимации в ячейке ниже.

Комментарии:

1. Да, я исправил проблемы с анимацией. Мой метод в чем-то похож на ваш, но я все еще не могу сделать так, чтобы он полностью выглядел так, как у tweetbots