Держите uitableview статичным при вставке строк вверху - PullRequest
60 голосов
/ 25 ноября 2010

У меня есть tableView, в который я вставляю строки сверху.

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

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

Есть ли хороший способ добиться этого?

Обновление: я использую beginUpdate, затем insertRowsAtIndexPath, endUpdates.Вызова reloadData нет.

scrollToRowAtIndexPath переходит на верх текущей ячейки (сохраняется до добавления строк).

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

save tableView currentOffset. (Underlying scrollView method)
Add rows (beginUpdates,insert...,endUpdates) 
reloadData ( to force a recalulation of the scrollview size )
Recalculate the correct new offset from the bottom of the scrollview
setContentOffset (Underlying scrollview method)

Проблема в том, что reloadData заставляет scrollview / tableview начать короткую прокрутку, затем setContentOffset возвращает его в правильное место.

Есть ли способ заставить tableView работать с новым размером, не запуская отображение?

Завершение всего процесса в beginAnimation commitAnimation также мало чем помогает.

Обновление 2: это можетОбязательно сделайте это - посмотрите официальное приложение для твиттера, когда вы загружаете обновления.

Ответы [ 18 ]

48 голосов
/ 10 марта 2014

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

    ...
    CGSize beforeContentSize = self.tableView.contentSize;
    [self.tableView reloadData];
    CGSize afterContentSize = self.tableView.contentSize;

    CGPoint afterContentOffset = self.tableView.contentOffset;
    CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);
    self.tableView.contentOffset = newContentOffset;
    ...
24 голосов
/ 22 июля 2012
-(void) updateTableWithNewRowCount : (int) rowCount
{    
    //Save the tableview content offset 
    CGPoint tableViewOffset = [self.tableView contentOffset];                                                                                                            

    //Turn of animations for the update block 
    //to get the effect of adding rows on top of TableView
    [UIView setAnimationsEnabled:NO];

    [self.tableView beginUpdates];                    

    NSMutableArray *rowsInsertIndexPath = [[NSMutableArray alloc] init];        

    int heightForNewRows = 0;

    for (NSInteger i = 0; i < rowCount; i++) {

        NSIndexPath *tempIndexPath = [NSIndexPath indexPathForRow:i inSection:SECTION_TO_INSERT];
        [rowsInsertIndexPath addObject:tempIndexPath];

        heightForNewRows = heightForNewRows + [self heightForCellAtIndexPath:tempIndexPath];                        
    }

    [self.tableView insertRowsAtIndexPaths:rowsInsertIndexPath withRowAnimation:UITableViewRowAnimationNone];                                                                            

    tableViewOffset.y += heightForNewRows;                                                                                 

    [self.tableView endUpdates]; 

    [UIView setAnimationsEnabled:YES];

    [self.tableView setContentOffset:tableViewOffset animated:NO];           
}


-(int) heightForCellAtIndexPath: (NSIndexPath *) indexPath
{

    UITableViewCell *cell =  [self.tableView cellForRowAtIndexPath:indexPath];

    int cellHeight   =  cell.frame.size.height;

    return cellHeight;
}

Просто введите количество новых строк, чтобы вставить их сверху.

21 голосов
/ 15 июля 2011

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

Один правильный путь: Используйте подкласс UITableView и переопределите -setContentSize: , в котором вы можете каким-то образом рассчитать, насколько табличное представление будет сдвинуто и смещено, установив contentOffset .

Это самый простой пример кода для обработки простейшей ситуации, когда все вставки происходят в верхней части табличного представления:

@implementation MyTableView

- (void)setContentSize:(CGSize)contentSize {
        // I don't want move the table view during its initial loading of content.
    if (!CGSizeEqualToSize(self.contentSize, CGSizeZero)) {
        if (contentSize.height > self.contentSize.height) {
            CGPoint offset = self.contentOffset;
            offset.y += (contentSize.height - self.contentSize.height);
            self.contentOffset = offset;
        }
    }
    [super setContentSize:contentSize];
}

@end
17 голосов
/ 16 декабря 2010

имел ту же проблему и нашел решение.

    save tableView currentOffset. (Underlying scrollView method)
    //Add rows (beginUpdates,insert...,endUpdates) // don't do this!
    reloadData ( to force a recalulation of the scrollview size )
    add newly inserted row heights to contentOffset.y here, using tableView:heightForRowAtIndexPath:
    setContentOffset (Underlying scrollview method)

как это:

- (CGFloat) firstRowHeight
{
    return [self tableView:[self tableView] heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}

...
CGPoint offset = [[self tableView] contentOffset];
[self tableView] reloadData];
offset.y += [self firstRowHeight];
if (offset.y > [[self tableView] contentSize].height) {
    offset.y = 0;
}
[[self tableView] setContentOffset:offset];
...

работает отлично, без глюков.

9 голосов
/ 03 февраля 2012

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

static CGPoint  delayOffset = {0.0};

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
    if ( animateChanges )
        [self.tableView beginUpdates];
    delayOffset = self.tableView.contentOffset; // get the current scroll setting
}

Добавлено это в точках вставки ячейки.Вы можете сделать вычитание дубликата для удаления ячейки.

    case NSFetchedResultsChangeInsert:

        delayOffset.y += self.tableView.rowHeight;  // add for each new row
        if ( animateChanges )   
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
        break;

и, наконец,

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    if ( animateChanges )   
    {
        [self.tableView setContentOffset:delayOffset animated:YES];
        [self.tableView endUpdates];
    }
    else
    {
        [self.tableView reloadData];
        [self.tableView setContentOffset:delayOffset animated:NO];
    }
}

При animateChanges = NO, я не мог видеть, как что-то двигалось, когда были добавлены ячейки.

При тестировании с animateChanges = YES "дрожание" было там.Похоже, анимация вставки ячеек не имела такой же скорости, как прокрутка анимированных таблиц.В то время как результат в конце может заканчиваться видимыми ячейками именно там, где они начинаются, кажется, что вся таблица переместится на 2 или 3 пикселя, а затем вернется назад.

Если скорость анимации можно сделать равной, она может остаться на месте.

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

5 голосов
/ 08 июня 2015

@ Дин,

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

[tableView beginUpdates];
[UIView setAnimationsEnabled:NO];

// ...

[tableView endUpdates];

[tableView setContentOffset:newOffset animated:NO];
[UIView setAnimationsEnabled:YES];
4 голосов
/ 16 ноября 2012

Каждый любит копировать и вставлять примеры кода, так что вот реализация ответа Андрея З.

Это по моему delegateDidFinishUpdating:(MyDataSourceDelegate*)delegate методу

if (self.contentOffset.y <= 0)
{
    [self beginUpdates];
    [self insertRowsAtIndexPaths:insertedIndexPaths withRowAnimation:insertAnimation];
    [self endUpdates];
}
else
{
    CGPoint newContentOffset = self.contentOffset;
    [self reloadData];

    for (NSIndexPath *indexPath in insertedIndexPaths)
        newContentOffset.y += [self.delegate tableView:self heightForRowAtIndexPath:indexPath];

    [self setContentOffset:newContentOffset];

    NSLog(@"New data at top of table view");
}

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

3 голосов
/ 09 июня 2013

Я сталкивался с ситуацией, когда существует много разделов, которые могут иметь различное количество строк между вызовами -reloadData из-за пользовательской группировки, и высота строк варьируется. Так что вот решение на основе AndreyZ. Это свойство contentHeight UIScrollView до и после -reloadData, и оно кажется более универсальным.

CGFloat contentHeight = self.tableView.contentSize.height;
CGPoint offset = self.tableView.contentOffset;
[self.tableView reloadData];

offset.y += (self.tableView.contentSize.height - contentHeight);
if (offset.y > [self.tableView contentSize].height)
    offset.y = 0;

[self.tableView setContentOffset:offset];
2 голосов
/ 18 января 2018

Я хочу добавить дополнительное условие. Если ваш код в iOS11 или более, вам нужно сделать, как показано ниже;

В iOS 11 представления таблиц по умолчанию используют приблизительные высоты. Это означает, что contentSize изначально является оценочным значением. Если вам нужно использовать contentSize, вы захотите отключить оценочную высоту, установив 3 свойства оценочной высоты на ноль:

tableView.estimatedRowHeight = 0 tableView.estimatedSectionHeaderHeight = 0 tableView.estimatedSectionFooterHeight = 0

1 голос
/ 04 ноября 2015

Простое решение для отключения анимации

func addNewRows(indexPaths: [NSIndexPath]) {
    let addBlock = { () -> Void in
        self.tableView.beginUpdates()
        self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .None)
        self.tableView.endUpdates()
    }

    tableView.contentOffset.y >= tableView.height() ? UIView.performWithoutAnimation(addBlock) : addBlock()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...