Как определить, что анимация закончилась на UITableView beginUpdates / endUpdates?

116

Я вставляю / удаляю ячейку таблицы, используя insertRowsAtIndexPaths/deleteRowsAtIndexPathsупаковку beginUpdates/endUpdates. Я также использую beginUpdates/endUpdatesпри настройке rowHeight. По умолчанию все эти операции анимированы.

Как я могу определить, что анимация закончилась при использовании beginUpdates/endUpdates?

pixelfreak
источник
1
К вашему сведению: это также относится и к -scrollToRowAtIndexPath:atScrollPosition:animated:.
Бен Лахман

Ответы:

292

Как насчет этого?

[CATransaction begin];

[CATransaction setCompletionBlock:^{
    // animation has finished
}];

[tableView beginUpdates];
// do some work
[tableView endUpdates];

[CATransaction commit];

Это работает, потому что анимация tableView использует CALayerанимацию внутри. То есть они добавляют анимацию к любому открытому CATransaction. Если открытых не CATransactionсуществует (нормальный случай), то неявно начинается одно, которое заканчивается в конце текущего цикла выполнения. Но если вы начнете один, как здесь, то он будет использовать его.

Рудольф Адамкович
источник
7
У меня не сработало. Блок завершения вызывается во время выполнения анимации.
mrvincenzo
2
@MrVincenzo Вы установили completionBlockранее beginUpdatesи endUpdatesнравится во фрагменте?
Рудольф Адамкович
2
[CATransaction commit]следует называть после, а не до [tableView endUpdates].
Рудольф Адамкович
6
Блок завершения никогда не вызывается, если ячейка содержит представление с анимацией, например UIActivityIndicatorView.
markturnip
3
Спустя столько времени я этого не знал. Чистое золото !! Нет ничего хуже, чем увидеть, как красивую анимацию убивает необходимое зло tableView.reloadData ().
DogCoffee
31

Быстрая версия


CATransaction.begin()

CATransaction.setCompletionBlock({
    do.something()
})

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
Майкл
источник
6

Если вы ориентируетесь на iOS 11 и выше, вам следует использовать UITableView.performBatchUpdates(_:completion:)вместо этого:

tableView.performBatchUpdates({
    // delete some cells
    // insert some cells
}, completion: { finished in
    // animation complete
})
Роберт
источник
5

Возможным решением может быть наследование от UITableView, для которого вы вызываете, endUpdatesи перезапись его setContentSizeMethod, поскольку UITableView настраивает размер своего содержимого в соответствии с добавленными или удаленными строками. Этот подход также должен работать для reloadData.

Чтобы уведомление отправлялось только после endUpdatesвызова, можно также перезаписать endUpdatesи установить там флаг.

// somewhere in header
@private BOOL endUpdatesWasCalled_;

-------------------

// in implementation file

- (void)endUpdates {
    [super endUpdates];
    endUpdatesWasCalled_ = YES;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];

    if (endUpdatesWasCalled_) {
        [self notifyEndUpdatesFinished];
        endUpdatesWasCalled_ = NO;
    }
}
Antoni
источник
5

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

- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
    [UIView animateWithDuration:0.0 animations:^{

        [tableView beginUpdates];
        if (operation)
            operation();
        [tableView endUpdates];

    } completion:^(BOOL finished) {

        if (completion)
            completion(finished);
    }];
}

Кредиты на https://stackoverflow.com/a/12905114/634940 .

Зденек
источник
4
Этот код у меня не работал. Блок завершения был вызван после endUpdates, но до завершения анимации.
Тим Кембер
1
Это не работает. Анимация tableview перестала работать при применении этого примера.
Primoz990
Попробуйте увеличить продолжительность (например, 0,3 секунды) - это должно сработать (у меня это сработало)
emem
1

Пока не нашел хорошего решения (если не считать подкласса UITableView). Решил performSelector:withObject:afterDelay:пока использовать . Не идеально, но выполняет свою работу.

ОБНОВЛЕНИЕ : похоже, я могу использовать scrollViewDidEndScrollingAnimation:для этой цели (это относится к моей реализации, см. Комментарий).

pixelfreak
источник
1
scrollViewDidEndScrollingAnimationвызывается только в ответ на setContentOffsetandscrollRectToVisible
samvermette 08
@samvermette Ну, в моем случае, когда вызывается beginUpdates / endUpdates, UITableView всегда анимирует-прокручивает. Но это относится к моей реализации, поэтому я согласен, что это не лучший ответ, но он работает для меня.
pixelfreak
Кажется, можно заключить обновления в блок анимации UIView. См. Мой ответ ниже: stackoverflow.com/a/14305039/634940 .
Zdenek
0

Вы можете использовать tableView:willDisplayCell:forRowAtIndexPath:как:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"tableView willDisplay Cell");
    cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}

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


Почему бы просто не вызвать метод, который вы хотите вызвать, когда анимация заканчивается после endUpdates?

- (void)setDownloadedImage:(NSMutableDictionary *)d {
    NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
    [indexPathDelayed addObject:indexPath];
    if (!([table isDragging] || [table isDecelerating])) {
        [table beginUpdates];
        [table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
        [table endUpdates];
        // --> Call Method Here <--
        loadingView.hidden = YES;
        [indexPathDelayed removeAllObjects];
    }
}
Чаун
источник
Спасибо, chown. Не думаю, willDisplayCellчто сработает. Мне нужно, чтобы это произошло после анимации, потому что визуальное обновление нужно делать только после того, как все уляжется.
pixelfreak 02
np @pixelfreak, если я подумаю о другом способе сделать это, я дам тебе знать.
chown