UITableView 行动画持续时间和完成回调

Posted

技术标签:

【中文标题】UITableView 行动画持续时间和完成回调【英文标题】:UITableView row animation duration and completion callback 【发布时间】:2011-04-19 10:47:05 【问题描述】:

有没有办法指定 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 【参考方案1】:

您可以尝试将 insertRowsAtIndexPath 包装在

- (void)beginUpdates
- (void)endUpdates

事务,然后做flash。

【讨论】:

见上面karwag的回答。你需要解决什么算“后来”的问题。【参考方案2】:

刚刚遇到这个。操作方法如下:

Objective-C

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

斯威夫特

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock 
    // Code to be executed upon completion

tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()

【讨论】:

再次,在这里完美运行。 ios6 和所有。这是一种适当的 SDK 支持机制,用于覆盖默认动画中的属性。也许您的 CATransaction 中有额外的、运行时间更长的动画?你知道,它们是嵌套的。 在 iOS6 中非常适合我。谢谢! setAnimationDuration 似乎不会影响插入/删除持续时间。 iOS 6 关于如何更改持续时间的任何建议? CATransaction setAnimationDuration: 似乎没有什么区别。 在 iOS 5.1.1、6.1、7.0 中也适用于我;但是,如果您需要在动画后获得一个新的 tableView.contentSize(就像我的情况一样),您必须使用 [self performSelectorOnMainThread:withObject:waitUntilDone:];在 setCompletionBlock 中,以便在下一个运行循环中调用您的委托。如果您直接调用您的委托,而不使用 performSelectorOnMainThread,您将获得 tableView.contentSize 的旧值。【参考方案3】:

扩展 karwag's fine answer,注意在 iOS 7 上,使用 UIView 动画围绕 CATransaction 提供对表格动画持续时间的控制。

[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 级别。

【讨论】:

动画持续时间似乎被忽略了。【参考方案4】:

缩短 Brent's fine answer,至少对于 iOS 7,您可以将这一切简洁地包装在 [UIView animateWithDuration:delay:options:animations:completion:] 调用中:

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

不过,除了 EaseInOut 之外,我似乎无法覆盖默认动画曲线。

【讨论】:

当以这种方式或@Brent 的方式进行行插入时,虽然持续时间得到尊重,但 UITableViewRowAnimation 似乎没有得到尊重,并且总是出现自上而下的动画,即使我指定了,例如UITableViewRowAnimationLeft。在 iOS 8.4 上进行测试 - 有人有解决方案吗?【参考方案5】:

对我来说,我需要这个作为 collectionView。我做了一个简单的扩展来解决这个问题:

extension UICollectionView 

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

        self.reloadSections(sections)

        CATransaction.commit()
    


【讨论】:

【参考方案6】:

这是karwag's answer 的 Swift 版本

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock  () -> Void in
    // your code here

tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()

【讨论】:

【参考方案7】:

这是一个非常有用的技巧! 我写了一个 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 中做到这一点 @Cyber​​Mew 是的,但是创建类别一直很痛苦,特别是由于额外文件的名称很长 只有ios 11才有,ios 10怎么用? @kemdo 为什么说它只在 iOS 11 中可用?这里的一切都是 iOS 2+ 除了setCompletionBlock 这是 iOS 4+【参考方案8】:

现在如果你想这样做,有新功能starting from iOS 11:

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

在更新闭包中放置与 beginUpdates()/endUpdates 部分相同的代码。并且完成在所有动画之后执行。

【讨论】:

这很棒。我没有注意到这个添加。【参考方案9】:

由于 tableView 的 performBatch 方法仅从 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()
        
    

【讨论】:

【参考方案10】:

Antoine's answer 非常好——但适用于 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        
)

【讨论】:

【参考方案11】:

如果有人在 tableView 忽略 UIView.animate 中的动画参数并使用“从上到下”默认动画重新加载行时遇到问题,我找到了一个奇怪的解决方案:

你需要:

    静音tableView动画 改用transitionAnimation

例子:

let indicesToUpdate = [IndexPath(row: 1, section: 0)]
UIView.transition(with: self.tableView,
                      duration: 0.5,
                      options: [.transitionCrossDissolve,
                                .allowUserInteraction,
                                .beginFromCurrentState],
                      animations: 
                        UIView.performWithoutAnimation 
                            self.tableView.reloadRows(at: indicesToUpdate,
                                                      with: .none)
                        
                      )

PS:UIView.transition(..) 也有可选的补全:)

【讨论】:

以上是关于UITableView 行动画持续时间和完成回调的主要内容,如果未能解决你的问题,请参考以下文章

回调:动画完成:在 .promise().done() 之后调用

真的不可能在异步线程中运行动画 GIF 吗?

有没有办法控制 UITableView scrollToRowAtIndexPath 上的动画?

UITableView beginUpdates/endUpdates 回调

是否可以在 Google Colaboratory 中进行动画可视化?

如何防止行动画向下滚动 TableHeaderView?