UITableView延迟reloadData直到动画完成

时间:2012-10-04 17:26:19

标签: ios uitableview animation reloaddata

我有一个代表项目清单的UITableView。每个项目都处于完成/撤消状态。

我想将所有已完成的项目保留在列表顶部,因此当用户点击撤消的项目时,我想知道该行应该去哪里(在当前结束时)完成项目列表)并将其移动到那里。

我正在尝试使用-moveRowAtIndexPath:toIndexPath:为我的UITableView执行此操作。

有时效果很好,其他时候也不太好。

当完成的动作在屏幕上的其他地方启动另一个动画时,它似乎运作良好。出于某种原因,这似乎是-reloadData电话的延迟。

如果不是这样(即,“发生”的唯一事情是标记为已完成并正在移动的行),动画似乎通过自动调用UITableView的-reloadData方法而短路。也就是说,动画开始,但大约一半时,调用-reloadData并且行捕捉到它们的最终位置。从用户的角度来看,这是非常不和谐的。

我已经通过我的代码进行了跟踪,以确认我自己没有打电话给-reloadData,而且似乎我没有触发此-reloadData电话。

我可以自动调用-reloadData,我理解为什么会调用它(尽管你认为它可能没有必要,但这是一个不同的问题),但我真的很喜欢它等到它完成动画。

这是我正在使用的代码:

NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex     inSection:0];

[tableView beginUpdates];

[checklist removeObject:task];
[checklist insertObject:task atIndex:newIndex];
[tableView moveRowAtIndexPath:oldPath toIndexPath:newPath];

[tableView endUpdates];

我搞砸了什么?

2 个答案:

答案 0 :(得分:1)

根据titaniumdecoy的要求,以下是我如何修复此问题:

我有一个UIViewController子类,其NSTimer每秒执行一次,检查基础数据源UITableView是否有更改,表明对-reloadData的调用是必要。所以,对于那个子类我添加了:

@property (BOOL) IsAnimating;

我最初将其设置为NO,如果isAnimating属性设置为YES,则NSTimer“短路”并跳过其正常处理。

因此,当我想运行此UITableView动画时,我将isAnimating属性设置为YES并运行动画。

然后,我安排选择器在将来运行1秒,将isAnimating重置为NO。然后,NSTimer将继续触发,并会看到isAnimating NO(最有可能在第二次调用-codeTimerFired后),然后找到数据源更新,开始致电reloadData

以下是暂停NSTimer处理和安排UITableView动画的代码:

// currentIndex and newIndex have already been calculated as the data item's
// original and destination indices (only 1 section in UITableView)
if (currentIndex != newIndex) {

    // This item's index has changed, so animate its movement
    NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex    inSection:0];
    NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex        inSection:0];

    // Set a flag to disable data source processing and calls to [UITableView reloadData] 
    [[self Owner] setIsAnimating:YES];

    // I haven't tested enough, but some documentation leads me to believe
    // that this particular call (-moveRowAtIndexPath:toIndexPath:) may NOT need
    // to be wrapped in a -beginUpdates/-endUpdates block
    [[[self Owner] InstructionsTableView] beginUpdates];

    // These lines move the item in my data source
    [[[MMAppDelegate singleton] CurrentChecklist] removeObject:[self CellTask]];
    [[[MMAppDelegate singleton] CurrentChecklist] insertObject:[self CellTask] atIndex:newIndex];

    // This code is the UITableView animation
    [[[self Owner] InstructionsTableView] moveRowAtIndexPath:oldPath toIndexPath:newPath];

    // Conclude the UITableView animation block
    [[[self Owner] InstructionsTableView] endUpdates];

    // Schedule a call to re-enable UITableView animation        
    [[self Owner] performSelector:@selector(setIsAnimating:) withObject:@(NO) afterDelay:1.0];

} else {
    // This location hasn't changed, so just tell my owner to reload its data
    [[[self Owner] InstructionsTableView] reloadData];
}

以下是NSTimer方法(注意isAnimating == YES如何解决问题):

- (void)codeTimerFired {

    // This is a subclass of a template subclass... 
    // super actually has work to do in this method...
    [super codeTimerFired];

    // If we're in the middle of an animation, don't update!
    if ([self IsAnimating]) {
        return;
    }

    // Other data source processing...

    // local BOOL to check whether underlying data source has changed
    BOOL shouldUpdate = NO;

    // code to check if underlying data source has changed...
    // ******************************************************
    // [CODE REMOVED]
    // ******************************************************

    // If the underlying data source has changed, update the UITableView
    if (shouldUpdate) {
        [self reloadTableView]; // <--- This is the main line I wanted to prevent
                                //      since the code that fired to cause the 
                                //      UITableView animation will ALWAYS cause
                                //      the underlying data source to change such
                                //      that this line would fire.
    }
}

答案 1 :(得分:1)

<强>夫特

//---------
extension UITableView {

    // Default delay time = 0.5 seconds
    // Pass animation time interval, as a parameter argument
    func reloadDataAfterDelay(delayTime: TimeInterval = 0.5) -> Void {
        self.perform(#selector(self.reloadData), with: nil, afterDelay: delayTime)
    }

}