1

我有一个 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];

我在搞砸什么吗?

4

2 回答 2

2

迅速

//---------
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)
    }

}
于 2017-07-10T13:52:57.060 回答
1

应钛诱饵的要求,这是我解决此问题的方法:

我有一个UIViewController子类,NSTimer它每秒执行一次,检查底层数据源中是否有UITableView表明需要调用的更改-reloadData。所以,我在那个子类中添加了:

@property (BOOL) IsAnimating;

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

所以,当我想运行这个UITableView动画时,我将isAnimating属性设置为YES并运行动画。

然后,我安排一个选择器在未来运行 1 秒,然后重置isAnimatingNO. 然后NSTimer将继续触发并会看到一个isAnimatingof 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方法(注意它是如何退出 if的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.
    }
}
于 2012-10-05T00:43:28.783 回答