3

我正在开发一个带有 UITableView 的 iOS 应用程序,它在某个阶段添加或删除了一堆行。由于存在大量行,因此此操作可能需要很长时间。但是,我不能轻易确定它是否需要很长时间。

仅当此操作需要很长时间时,我才想显示 UIActivityIndi​​cator (微调器)。我一直这样做的方式是启动冗长的操作,经过一些延迟(比如 0.5 秒)后,我们测试操作是否仍在运行,如果是,我们开始显示 UIActivityIndi​​cator。

如果您可以在后台线程中运行冗长的操作,这没有问题。但是,这种特殊情况很棘手,因为冗长的操作 (deleteRowsAtIndexPaths:withRowAnimation:) 必须在主线程中运行(如果我在后台线程中运行此方法,当后台线程尝试更新 UI 时,应用程序偶尔会崩溃)。

我尝试过的最新事情是这样的:

- (void) manipulateTableView
{
    stillBusy = YES;
    [self performSelectorInBackground:@selector(waitBeforeShowingBusyIndicatorForView:) withObject:view];
    .
    .
    .
    [self performSelectorOnMainThread:@selector(deleteRowsAtIndexPathsWithRowAnimation:) withObject:args waitUntilDone:YES];
    stillBusy = NO;
}

- (void) waitBeforeShowingBusyIndicatorForView:(UIView*) view
{
    usleep((int) (BUSY_INDICATOR_DELAY * 1000000));
    if (stillBusy)
        [self performSelectorOnMainThread:@selector(showBusyIndicatorForView:) withObject:view waitUntilDone:NO];
}

因为 showBusyIndi​​catorForView: 操作 UI,所以必须在主线程调用,否则应用可能会崩溃。

当 deleteRowsAtIndexPaths:withRowAnimation: 耗时很长时,waitBeforeShowingBusyIndi​​catorForView: 中的延迟到期,调用 performSelectorOnMainThread:... 方法并立即返回。但是只有在对 deleteRowsAtIndexPaths:withRowAnimation: 的调用完成后才调用 showBusyIndi​​catorForView: 方法,这违背了目的。

我想我明白为什么会发生这种情况。deleteRowsAtIndexPaths:withRowAnimation: 方法在主循环的迭代中运行,当它运行时,对 showBusyIndi​​catorForView: 的调用作为主循环的消息排队。只有在主循环完成执行 deleteRowsAtIndexPaths:withRowsAnimation: 后,它才会轮询队列以获取下一条消息,然后开始执行 showBusyIndi​​catorForView:。

有没有办法让这个东西正常工作。是否有可能中断主循环并使其立即执行 showBusyIndi​​catorForView: ?

4

3 回答 3

3

两个选项突然出现在我面前:

  1. 修改您长时间运行的前台例程,以便将其分成多个块,您可以重复地将进程的每个步骤分别分派到主队列。并以您设计流程的方式执行此操作,以便它分派慢速前台任务的每个部分,并且仅在前一部分完成时将下一步分派到主队列。(不要只为主队列排队一大堆进程。)这样,当微调器排队时,您就有机会与其他作业相吻合。如果您要为删除设置动画,那可能是不可能的,但话又说回来,我无法想象做一个deleteRowsAtIndexPaths.

  2. 可能更好,如果更新需要那么长时间,我会问为什么会这样。deleteRowsAtIndexPaths不应该解释这一点。具体来说,不要发出多个deleteRowsAtIndexPaths,而是构建NSMutableArray要删除的行,然后在单个 UI 调用中删除它们。我刚刚删除了 998 行(包括它们的底层模型对象),这几乎是瞬间完成的。

如果可能的话,我会倾向于选项 2。

于 2013-01-15T22:50:14.953 回答
0

当主线程上运行冗长的操作时,您无法更新 UI。

如果您的延迟确实是由-deleteRowsAtIndexPaths:withRowAnimation:(如果您要求表格视图以动画方式删除几千行)引起的延迟,那么最好的解决方案是避免动画超过某个(相对较小)阈值和只是打电话-reloadData

于 2013-01-15T23:03:14.343 回答
0

罗布,谢谢你的建议!它帮助我找到了解决方案!

首先,为了记录,我只为所有行调用了一次 deleteRowsAtIndexPaths (数千行:O!)。

我所做的关键观察是,当动画开始时,屏幕上只有少数行可见。特别是该部分的顶部行,因为通过点击该部分的标题来触发行的删除。因此,行不必都是动画的一部分。我可以先删除所有不可见的行(没有动画),然后只删除带有动画的可见行。

然后我对 deleteRowsAtIndexPaths 进行了两次调用——第一次删除了所有不可见的行,第二次删除了可见(顶部)的行。现在第一次调用 deleteRowsAtIndexPaths 花了很长时间,因为它必须删除数千行,并且活动指示器视图再次没有机会开始动画。然后,我将行删除的第一阶段更改为一次删除行 200 的循环,如下所示:

for ( /* next range of invisible rows */ )
{
    // Compute index paths of the bottom 200 rows.
    [self performSelectorOnMainThread:@selector(deleteRowsAtIndexPathsWithRowAnimation:) withObject:args waitUntilDone:YES];
}

然后,启动活动指示器动画的方法将在两个后续调用 deleteRowsAtIndexPaths…: 之间排队。

有趣的是:现在我一次删除 200 行,这需要相当长的时间,但它仍然比我一次删除它们要快得多!:|

于 2013-01-16T18:43:54.697 回答