Прокрутка UITableView до верхней части обновления ячейки

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

- (void) createNoteCellViewDidUpdate: (CreateNoteCell*) cell {

    CGFloat newHeight = [self tableView:self.tableView heightForRowAtIndexPath:CreateNoteCellIndexPath];
    if (newHeight != CGRectGetHeight(cell.contentView.frame)) {
        [self.tableView beginUpdates];
        [self.tableView endUpdates]; // <- HERE IS WHERE THE CONTENT OFFSET CHANGES
    }
}

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

table scrolling on cell resize

По наблюдению за ключевым значением, я обнаружил, что представление содержимого прокруткиOffset устанавливается при изменении вида содержимого прокрутки. И что contentSize меняет много раз, когда вызывается метод endUpdates. Когда он меняется, он идет от нормальной высоты, до довольно большой высоты, а затем возвращается к нормальной высоте. Интересно, будет ли это из-за этого. Я не знаю, куда идти отсюда.

Ответ 1

Ничего себе, ответ Джоша Гафни очень помог; он заслуживает продолжения. Мое решение основано на его. Это по-прежнему кажется взломом, поэтому, если у кого-то еще есть лучший способ сделать это, мне все равно очень интересно это услышать.

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

CGPoint offset = self.tableView.contentOffset;
[self.tableView beginUpdates];
[self.tableView endUpdates];

Это оживляет изменение высоты ячейки и настраивает все кадры в соответствии с ограничениями.

Затем я удаляю все анимации из слоя tableView, поэтому анимация на вершине останавливается. Тогда я reset contentOffst к тому, что было раньше.

[self.tableView.layer removeAllAnimations];
[self.tableView setContentOffset:offset animated:NO];

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

[self.tableView scrollToRowAtIndexPath:CreateNoteCellIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

И вуаля, это работает очень хорошо.

fixed cell resize

Ответ 2

Просто догадаться...

Сохраните contentOffset uitableview перед любыми изменениями. Затем установите его снова.

CGFloat contentOffset = self.tableView.contentOffset;

а затем после изменений

self.tableView.contentOffset = contentOffset;

Ответ 3

Вот мое расширение UITableView, которое отлично работает. В Swift 2.1. На основании принятого ответа.

extension UITableView
{
    func reloadDataAnimatedKeepingOffset()
    {
        let offset = contentOffset
        UIView.setAnimationsEnabled(false)
        beginUpdates()
        endUpdates()
        UIView.setAnimationsEnabled(true)
        layoutIfNeeded()
        contentOffset = offset
    }
}

Ответ 4

Более упрощенное решение (добавьте наблюдателя уведомлений при изменении текста), Swift-версию. Основной трюк использует UIView.setAnimationsEnabled.

func textViewDidChanged(notification: NSNotification) {
    UIView.setAnimationsEnabled(false)

    let contentOffset = self.tableView.contentOffset

    tableView.beginUpdates()
    tableView.endUpdates()

    tableView.contentOffset = contentOffset

    UIView.setAnimationsEnabled(true)
}