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

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

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

21 ответ

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

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

[tableView beginUpdates];
[tableView endUpdates];

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

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

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

Создайте 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) выглядела выделенной по умолчанию.

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

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

Вместо beginUpdates()/endUpdates(), рекомендуемый вызов сейчас:

tableView.performBatchUpdates(nil, completion: nil)

Apple говорит в отношении beginUpdates/endUpdates: "При возможности используйте метод executeBatchUpdates(_: завершение:) вместо этого".

См.: https://developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates

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

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

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

Я решил с reloadRowsAtIndexPaths,

Я экономлю в didSelectRowAtIndexPath indexPath выбранной ячейки и вызов 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];
}

Свифт 4 и выше

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

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()
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;
}

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

@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;
}

Вот более короткая версия ответа Саймонса для 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
  }

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

[tableView beginUpdates];
[tableView endUpdates];

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

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

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: так что обращайтесь соответственно.

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

// 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()
            }
        }
    }
}

Входы -

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

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

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

Он вызовет функцию tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} этой функции.

Я использовал потрясающий ответ @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];

}

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

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

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

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

Вот мой код кастома 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;
}

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

tableView.beginUpdates()
tableView.endUpdates()

Имейте в виду, что вы должны изменить свойства высоты ДО endUpdates(),

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

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:-)

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

Другие вопросы по тегам