Продолжительность анимации строки UITableView и обратный вызов завершения

99

Есть ли способ указать продолжительность для анимации строки UITableView или получить обратный вызов после завершения анимации?

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

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];
Дэниел Дикисон
источник
Я не пробовал себя, но, возможно, это могло бы сделать это с некоторой обработкой пути индекса:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle

Ответы:

3

В настоящее время, если вы хотите сделать это, начиная с iOS 11 есть новая функция :

- (void)performBatchUpdates:(void (^)(void))updates 
                 completion:(void (^)(BOOL finished))completion;

В закрытии обновлений вы размещаете тот же код, что и в разделе beginUpdates () / endUpdates. И завершение выполняется после всех анимаций.

Михал Зиобро
источник
Это круто. Я не заметил этого дополнения.
Дэниел Дикисон
207

Только что наткнулся на это. Вот как это сделать:

Цель-C

[CATransaction begin];
[tableView beginUpdates];
[CATransaction setCompletionBlock: ^{
    // Code to be executed upon completion
}];
[tableView insertRowsAtIndexPaths: indexPaths
                 withRowAnimation: UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[CATransaction commit];

Swift

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock {
    // Code to be executed upon completion
}
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()
Карваг
источник
2
Опять же, здесь работает безупречно. iOS6 и все. Это правильный поддерживаемый SDK механизм для переопределения свойств в анимации по умолчанию. Возможно, у вас есть дополнительные, более длительные анимации внутри CATransaction? Вы знаете, они вкладываются друг в друга.
karwag
1
У меня отлично работает в iOS6. Спасибо за это!
Aron
5
setAnimationDurationпохоже, не влияет на продолжительность вставки / удаления. iOS 6
Том Редман
2
какие-нибудь предложения о том, как изменить продолжительность? CATransaction setAnimationDuration: похоже, не имеет значения.
Джефф Граймс
5
У меня тоже отлично работает в iOS 5.1.1, 6.1, 7.0; Но если вам нужно получить новый tableView.contentSize после анимации (как это было в моем случае), вы должны использовать [self performSelectorOnMainThread: withObject: waitUntilDone:]; в setCompletionBlock, чтобы вызвать вашего делегата в следующем цикле выполнения. если вы вызываете своего делегата напрямую, без performSelectorOnMainThread, вы получаете старое значение для tableView.contentSize.
Slamor
38

Расширяя прекрасный ответ karwag , обратите внимание, что в iOS 7 окружение CATransaction с помощью UIView Animation предлагает управление продолжительностью анимации таблицы.

[UIView beginAnimations:@"myAnimationId" context:nil];

[UIView setAnimationDuration:10.0]; // Set duration here

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    NSLog(@"Complete!");
}];

[myTable beginUpdates];
// my table changes
[myTable endUpdates];

[CATransaction commit];
[UIView commitAnimations];

Продолжительность анимации UIView не влияет на iOS 6. Возможно, анимация таблиц iOS 7 реализована по-другому, на уровне UIView.

Brent
источник
Кажется, что продолжительность анимации игнорируется.
Дастин
26

Это чертовски полезный трюк! Я написал расширение UITableView, чтобы все время не писать материалы CATransaction.

import UIKit

extension UITableView {

    /// Perform a series of method calls that insert, delete, or select rows and sections of the table view.
    /// This is equivalent to a beginUpdates() / endUpdates() sequence, 
    /// with a completion closure when the animation is finished.
    /// Parameter update: the update operation to perform on the tableView.
    /// Parameter completion: the completion closure to be executed when the animation is completed.

    func performUpdate(_ update: ()->Void, completion: (()->Void)?) {

        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        // Table View update on row / section
        beginUpdates()
        update()
        endUpdates()

        CATransaction.commit()
    }

}

Это используется так:

// Insert in the tableView the section we just added in sections
self.tableView.performUpdate({
            self.tableView.insertSections([newSectionIndex], with: UITableViewRowAnimation.top)

        }, completion: {
            // Scroll to next section
            let nextSectionIndexPath = IndexPath(row: 0, section: newSectionIndex)
            self.tableView.scrollToRow(at: nextSectionIndexPath, at: .top, animated: true)
        })
Фредерик Адда
источник
Отличный ответ! это одна из причин, почему я люблю Swift
Джанни Карло
@GianniCarlo, вы можете сделать это и в ObjC
CyberMew
@CyberMew: да, но создание категории всегда было такой головной болью, особенно из-за длинных названий дополнительных файлов
Джанни Карло
он доступен только в ios 11, как его использовать в ios 10?
kemdo
@kemdo Почему вы говорите, что он доступен только в iOS 11? Здесь все iOS 2+, кроме setCompletionBlockiOS 4+
Фредерик Адда
25

Сокращая прекрасный ответ Брента , по крайней мере, для iOS 7 вы можете кратко обернуть все это в вызове [UIView animateWithDuration: delay: options: animations: завершения:]:

[UIView animateWithDuration:10 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  [self.tableView beginUpdates];
  [self.tableView endUpdates];
} completion:^(BOOL finished) {
  // completion code
}];

хотя, похоже, я не могу изменить кривую анимации по умолчанию ни с чем, кроме EaseInOut.

Вишну
источник
2
При вставке строки таким способом или способом @Brent, хотя продолжительность соблюдается, UITableViewRowAnimation, похоже, не соблюдается и всегда кажется анимированным сверху вниз, даже когда я указываю, например, UITableViewRowAnimationLeft. Тестирование на iOS 8.4 - у кого-нибудь есть решение?
Дэнни
23

Вот быстрый вариант ответа Карвага

    CATransaction.begin()
    tableView.beginUpdates()
    CATransaction.setCompletionBlock { () -> Void in
        // your code here
    }
    tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
    tableView.endUpdates()
    CATransaction.commit()
первобытный
источник
7

Мне это нужно для collectionView. Я сделал простое расширение, чтобы решить эту проблему:

extension UICollectionView {

    func reloadSections(sections: NSIndexSet, completion: () -> Void){
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        self.reloadSections(sections)

        CATransaction.commit()
    }

}
Антуан
источник
1

Поскольку performBatchметод tableView доступен только с iOS 11 , вы можете использовать следующее расширение:

extension UITableView {
func performUpdates(_ updates: @escaping () -> Void, completion: @escaping (Bool) -> Void) {
        if #available(iOS 11.0, *) {
            self.performBatchUpdates({
                updates()
            }, completion: completion)
        } else {
            CATransaction.begin()
            beginUpdates()
            CATransaction.setCompletionBlock {
                completion(true)
            }
            updates()
            endUpdates()
            CATransaction.commit()
        }
    }
}
Станислав Барановский
источник
0

Ответ Антуана довольно хорош, но для UICollectionView. Вот он для UITableView:

extension UITableView {
    func reloadSections(_ sections: IndexSet, with rowAnimation: RowAnimation, completion: (() -> Void)?) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        
        self.reloadSections(sections, with: rowAnimation)
        
        CATransaction.commit()
    }
}

Называется так:

tableView.reloadSections(IndexSet(0), with: .none, completion: {
    // Do the end of animation thing        
})
будущее-адам
источник
-8

Вы можете попробовать обернуть insertRowsAtIndexPath в

- (void)beginUpdates
- (void)endUpdates

транзакции, а затем выполните флэш-память.

Иордания
источник
См. Ответ Карвага выше. Вам нужно решить проблему того, что считается «потом».
JLundell