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

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

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

Enter image description here

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

    //if user tapped the same row twice let 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 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;
}

Также верните соответствующую высоту для ControlCell.

- (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 && indexPath.row > whereIsTheControlRow)
        return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0]; 
    return indexPath;
}

Надеюсь, это поможет.

Ответ 3

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

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

  • В tableviewcells есть фоновое изображение. В противном случае добавленная панель subview/toolbar видна через ячейку непосредственно перед тем, как табличное представление оживляет изменение высоты.
  • Tableviewcell находится "за" вид, который является ячейкой под ним, иначе снова появится панель subview/toolbar. Я использую [tableview sendSubviewToBack: cell]; и он заботится об этом.

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

Ответ 6

@rjgonzo это отлично работает, но есть небольшая проблема с тем, как вы храните indexPathToDelete. Поскольку это просто еще один указатель на self.controlRowIndexPath, как только вы очистите или переназначаете self.controlRowIndexPath, indexPathToDelete не будет того, что вы хотели, а tableView deleteRowsAtIndexPaths: 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];
}