Можете ли вы анимировать изменение высоты на UITableViewCell при выборе?


393

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

Это возможно?

Ответы:


879

Я нашел ДЕЙСТВИТЕЛЬНО ПРОСТОЕ решение этой проблемы как побочный эффект, над которым UITableViewя работал .....

Сохраните высоту ячейки в переменной, которая обычно сообщает исходную высоту через tableView: heightForRowAtIndexPath:, затем, когда вы хотите анимировать изменение высоты, просто измените значение переменной и вызовите this ...

[tableView beginUpdates];
[tableView endUpdates];

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

У меня есть более подробное объяснение и полные примеры кода в моем блоге ... Изменение высоты ячейки Animate UITableView


6
Это великолепно. Но есть ли способ контролировать скорость анимации таблицы?
Джош Кахане

7
Это работает, но если я увеличу ячейку, скажем, от 44 до 74, а затем снова уменьшу ее до 44, разделительная линия будет совершенно странной. Кто-нибудь может это подтвердить?
plaetzchen

46
Это причудливое решение, но это то, что Apple рекомендует и в сеансе WWDC 2010 «Просмотр таблицы мастеринга». Я собираюсь подать отчет об ошибке при добавлении этого в документацию, потому что я потратил около 2 часов на исследование.
Бпапа

5
Я пробовал это решение, и оно работает только иногда, когда вы нажимаете ячейку, вероятность срабатывания 50%. У кого-нибудь есть такая же ошибка? Это потому что iOS7?
Джоан Кардона

7
Теперь это также написано в официальной документации: You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell. developer.apple.com/library/ios/documentation/UIKit/Reference/…
Ярослав

63

Мне нравится ответ Саймона Ли. На самом деле я не пробовал этот метод, но, похоже, он изменит размер всех ячеек в списке. Я надеялся на изменение только той ячейки, которая прослушивается. Я вроде сделал это как Саймон, но с небольшой разницей. Это изменит внешний вид ячейки при ее выборе. И это оживляет. Просто еще один способ сделать это.

Создайте int для хранения значения для текущего выбранного индекса ячейки:

int currentSelection;

Затем:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = [indexPath row];
    selectedNumber = row;
    [tableView beginUpdates];
    [tableView endUpdates];
}

Затем:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    if ([indexPath row] == currentSelection) {
        return  80;
    }
    else return 40;


}

Я уверен, что вы можете внести аналогичные изменения в tableView: cellForRowAtIndexPath: изменить тип ячейки или даже загрузить файл XIB для ячейки.

Таким образом, currentSelection начнется с 0. Вам нужно будет внести коррективы, если вы не хотите, чтобы первая ячейка списка (с индексом 0) выглядела выделенной по умолчанию.


2
Проверьте код, прикрепленный к моему сообщению, я сделал именно это, удвоив высоту ячейки при выборе. :)
Саймон Ли

8
«На самом деле я не пробовал этот метод, но, похоже, он изменит размер всех ячеек в списке», - тогда вы не выглядели очень усердно.
Джейми

13
Текущий выбор уже сохранен в tableView.indexPathForSelectedRow.
Ник

22

Добавить свойство для отслеживания выбранной ячейки

@property (nonatomic) int currentSelection;

Установите его в значение часового значения (например) viewDidLoad, чтобы убедиться, что UITableViewпуски находятся в «нормальном» положении

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //sentinel
    self.currentSelection = -1;
}

В heightForRowAtIndexPathвы можете установить высоту, которую вы хотите для выбранной ячейки

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int rowHeight;
    if ([indexPath row] == self.currentSelection) {
        rowHeight = self.newCellHeight;
    } else rowHeight = 57.0f;
    return rowHeight;
}

В didSelectRowAtIndexPathвас сохранить текущий выбор и сохранить высоту динамического, если требуется

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // do things with your cell here

        // set selection
        self.currentSelection = indexPath.row;
        // save height for full text label
        self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

В didDeselectRowAtIndexPathустановить индекс выбора обратно к значению дозорного и анимировать спину клеток к нормальной форме

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {       
        // do things with your cell here

        // sentinel
        self.currentSelection = -1;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

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

14

reloadData не годится, потому что нет анимации ...

Вот что я сейчас пытаюсь:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

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


Лично я обнаружил, что использование этого метода, но с UITableViewRowAnimationNone обеспечивает более плавный, но все же не идеальный результат.
Рон Сребро

11

Я не знаю, что все эти вещи о последовательном вызове beginUpdates / endUpdates, вы можете просто использовать -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Вот пример проекта .


Отлично, это не растягивает мои текстовые представления в моей ячейке автоматического макета. Но это должно иметь мерцание для анимации, потому что опция None animation выглядит сбой при обновлении размера ячейки.
h3dkandi

10

Я решил с reloadRowsAtIndexPaths.

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

В heightForRowAtIndexPath вы можете проверить , если indexPath в списке или не expandIndexPath ячейки и высоты отправки.

Вы можете проверить этот базовый пример: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Это простое решение.

Я добавлю код, если вам поможет

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
    if ([indexPath isEqual:_expandIndexPath])
        return 80;

    return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Celda";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell.textLabel setText:@"wopwop"];

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *modifiedRows = [NSMutableArray array];
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];
    _expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];

    // This will animate updating the row sizes
    [tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}


3

Попробуйте это для расширения индексной строки:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
    return 100;

return 44;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
    [modifiedRows addObject:self.expandIndexPath];
    self.expandIndexPath = nil;
} else {
    if (self.expandedIndexPath)
        [modifiedRows addObject:self.expandIndexPath];

    self.expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];
}

// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];

// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];

return cell;
}

3
BOOL flag;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    flag = !flag;
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] 
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES == flag ? 20 : 40;
}

2

просто заметка для кого-то вроде меня, ищущего добавить "Подробнее" в пользовательской ячейке.

[tableView beginUpdates];
[tableView endUpdates];

Проделали отличную работу, но не забудьте «обрезать» вид клетки. В Интерфейсном Разработчике выберите «Ячейка» -> «Просмотр содержимого» -> из «Инспектора свойств» выберите « Подвид клипа »


2

Вот более короткая версия ответа Саймонса для Swift 3. Также позволяет переключать выбор ячейки

var cellIsSelected: IndexPath?


  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
  }


  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if cellIsSelected == indexPath {
      return 250
    }
    return 65
  }

2

Свифт 4 и выше

добавьте приведенный ниже код в метод делегата didselect строки tableview

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

1

Свифт версия ответа Саймона Ли.

// MARK: - Variables 
  var isCcBccSelected = false // To toggle Bcc.



    // MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    // Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
    if self.cellTypes[indexPath.row] == CellType.Bcc {
        if (isCcBccSelected) {
            return 44
        } else {
            return 0
        }
    }

    return 44.0
}

Затем в didSelectRowAtIndexPath ()

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

    // To Get the Focus of CC, so that we can expand Bcc
    if self.cellTypes[indexPath.row] == CellType.Cc {

        if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {

            if cell.tag == 1 {
                cell.recipientTypeLabel.text = "Cc:"
                cell.recipientTextField.userInteractionEnabled = true
                cell.recipientTextField.becomeFirstResponder()

                isCcBccSelected = true

                tableView.beginUpdates()
                tableView.endUpdates()
            }
        }
    }
}

1

Да, это возможно.

UITableView имеет метод делегата didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [UIView animateWithDuration:.6
                          delay:0
         usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
          initialSpringVelocity:0
                        options:UIViewAnimationOptionBeginFromCurrentState animations:^{

                            cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
                            NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
                            [violatedTableView beginUpdates];
                            [violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
                            [violatedTableView endUpdates];
                        }
                     completion:^(BOOL finished) {
    }];
}

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


0

Вот мой код пользовательского UITableViewподкласса, который расширяется UITextViewв ячейке таблицы без перезагрузки (и потерял фокус клавиатуры):

- (void)textViewDidChange:(UITextView *)textView {
    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    // Check, if text height changed
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
        [self beginUpdates];

        // Calculate difference in height
        CGFloat difference = textHeight - self.previousTextHeight;

        // Update currently editing cell's height
        CGRect editingCellFrame = self.editingCell.frame;
        editingCellFrame.size.height += difference;
        self.editingCell.frame = editingCellFrame;

        // Update UITableView contentSize
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);

        // Scroll to bottom if cell is at the end of the table
        if (self.editingNoteInEndOfTable) {
            self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
        } else {
            // Update all next to editing cells
            NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
            for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
                UITableViewCell *cell = self.visibleCells[i];
                CGRect cellFrame = cell.frame;
                cellFrame.origin.y += difference;
                cell.frame = cellFrame;
            }
        }
        [self endUpdates];
    }
    self.previousTextHeight = textHeight;
}

0

Я использовал потрясающий ответ @ Joy, и он отлично работал с ios 8.4 и XCode 7.1.1.

Если вы хотите, чтобы ваша ячейка переключалась, я изменил -tableViewDidSelect следующим образом:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell, 
//cell is expanded. If tapped again on the same cell, 
//cell is collapsed. 
    if (self.currentSelection==indexPath.row) {
        self.currentSelection = -1;
    }else{
        self.currentSelection = indexPath.row;
    }
        // animate
        [tableView beginUpdates];
        [tableView endUpdates];

}

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


0

Проверьте этот метод после iOS 7 и позже.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

Улучшения были сделаны в iOS 8. Мы можем установить его как свойство самого табличного представления.



0

Входы -

tableView.beginUpdates () tableView.endUpdates () эти функции не будут вызывать

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

Но, если вы это сделаете, tableView.reloadRows (в: [selectedIndexPath! As IndexPath], с: .none)

Эта функция вызовет func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} .


-1

Я только решил эту проблему с небольшим взломом:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;

- (void)onTimer {
    cellHeight++;
    [tableView reloadData];
    if (cellHeight < s_CellHeightEditing)
        heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}

- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
        if (isInEdit) {
            return cellHeight;
        }
        cellHeight = s_CellHeight;
        return s_CellHeight;
}

Когда мне нужно увеличить высоту ячейки, я устанавливаю isInEdit = YESи вызываю метод, [self onTimer]и он оживляет рост ячейки, пока не достигнет значения s_CellHeightEditing :-)


В симуляторе прекрасно работает, но в айфоне аппаратное обеспечение отстает. С задержкой таймера 0,05 и увеличением cellHeight на 5 единиц это намного лучше, но ничего похожего на CoreAnimation
Дзамир

1
Подтвердите, перед публикацией .. в следующий раз.
ajay_nasa

-2

Получить указатель пути выбранной строки. Перезагрузите стол. В методе heightForRowAtIndexPath объекта UITableViewDelegate установите высоту выбранной строки на другую высоту, а для остальных верните обычную высоту строки.


2
-1, не работает. Вызов [table reloadData]результатов в изменении высоты происходит мгновенно, а не анимация.
Марк Амери
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.