UIAlertController 不会立即显示

UIAlertController does not show immediately

提问人:Silverness 提问时间:5/28/2016 最后编辑:Silverness 更新时间:6/10/2016 访问量:1098

问:

Xcode版本:7.2.1,iOS版本:9.1+

我正在尝试在 iPad 上显示 UIAlertController 警报消息,显示“正在加载...请稍候“消息,没有任何按钮。我在开始长操作之前显示 UIAlertController,然后在长操作之后关闭 UIAlertController。但是,正在发生的事情是 UIAlertController 不会立即显示。它只会在较长的操作完成后短暂闪烁,然后被关闭。

以下是一般代码结构:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Loading" message:@"Please wait...\n\n\n" preferredStyle:UIAlertControllerStyleAlert];
    [self presentViewController:alert animated:YES completion:nil];

    /// Perform long operation here...
    /// (Basically, a message is being sent to a server, a long operation happens on 
    /// the server, and then the server returns a response to the iPad client.)

    [alert dismissViewControllerAnimated:YES completion:nil];
}

我尝试了许多替代方法,例如使用 dispatch_async以便在发生长时间操作时可以同时显示警报消息:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Loading Dataset" message:@"Please wait...\n\n\n" preferredStyle:UIAlertControllerStyleAlert];
    [self presentViewController:alert animated:YES completion:nil];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{          

        /// Perform long operation here...

        dispatch_async(dispatch_get_main_queue(), ^{

            [alert dismissViewControllerAnimated:YES completion:nil];
        });
    });
}

我尝试单独使用UIAlertView(我知道在iOS 8中已弃用),无论是单独使用还是与调度一起使用,但这也不起作用。

我尝试将长操作代码包装在performSelector消息中,但无济于事。

过去,将 UIAlertView 与 dispatch_queue 一起使用可以无缝地解决这种情况。

任何帮助都是值得赞赏的。

编辑:

需要注意的一件有趣的事情:在dispatch_async代码中,如果我在调用长操作代码之前添加一个 usleep(250000),大约 80% 的时间,UIAlertController 警报消息将在正确的时间显示:在长操作开始之前。然而,这不是 100% 的时间,也不是可持续的解决方案。使用较小的数字调用 usleep 不起作用。

请注意,对于DISPATCH_QUEUE_PRIORITY_DEFAULT选项,我还尝试了: DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_LOW和DISPATCH_QUEUE_PRIORITY_BACKGROUND

而且,我也尝试了以下方法:

dispatch_queue_t myQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);

dispatch_async(myQueue, ^{
    /// Perform long operation here...
    dispatch_async(dispatch_get_main_queue(), ^{
        [alert dismissViewControllerAnimated:YES completion:nil];
    });
});
iOS9 UIAlerview UIAlerControlleriOS

评论

0赞 Sulthan 5/28/2016
您的第二个变体是正确的,您应该发布更多有关您的长期操作的代码。您可能在长操作中的某个位置阻塞了主线程。这恰恰会导致这个问题。
0赞 Silverness 5/28/2016
@Sulthan 是的,在长操作代码中,我使用 TCP 套接字来发送接收数据。recv 套接字函数在从服务器接收到数据之前会阻塞。
0赞 Sulthan 5/28/2016
您是在后台线程上执行此操作(在第二个版本中),因此这应该不是问题。我猜你也以某种方式阻塞了那里的主线程。

答:

0赞 Bhavuk Jain 5/28/2016 #1

尝试在显示警报控制器时将 CFRunLoopWakeUp(CFRunLoopGetCurrent()) 放在正下方。

评论

0赞 Silverness 5/28/2016
谢谢你的建议。我只是在这两种情况下都尝试过(有和没有调度),但它不起作用......它表现出相同的行为。
0赞 Bhavuk Jain 5/28/2016
我建议这个答案,因为你可能不在主线程中,这就是延迟的原因。
1赞 Silverness 6/1/2016 #2

最终起作用的是调度代码和 CFRunLoopWakeUp 调用(由 @Bhavuk Jain 建议)的组合。我还必须做的是将最初位于调度块之后的代码(我没有在原始帖子中显示)移动到调度块内部。因此,到目前为止,以下内容正在起作用:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    // Initialize and present the alert view controller.
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Loading Dataset" message:@"Please wait...\n\n\n" preferredStyle:UIAlertControllerStyleAlert];
    [self presentViewController:alert animated:YES completion:nil];

    // Force wake up the run loop.
    CFRunLoopWakeUp(CFRunLoopGetCurrent());

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{          

        /// Perform long operation here...

        dispatch_async(dispatch_get_main_queue(), ^{

            [alert dismissViewControllerAnimated:YES completion:nil];

            /// Execute rest of code INDSIDE the dispatch code block INSTEAD of after it...
        });
    });
}
1赞 Silverness 6/10/2016 #3

在我标记为答案的原始帖子(5 月 31 日)中,有几种情况下,AlertViewController 消息会挂起,因为 dismissViewController 方法不会被调用。(也许是各种线程中的一些竞争条件......我不确定。因此,我最终发现可靠地工作并且似乎是一个更简洁的解决方案,即结合使用 AlertViewController 的完成块和dispatch_after调用。

有关 iOS 参考中的完成调用的更多信息:“在提供的视图控制器上调用 viewDidAppear: 方法后,将调用完成处理程序。

(这篇文章很有帮助。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    // Initialize the alert view controller.
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Loading Dataset" message:@"Please wait...\n\n\n" preferredStyle:UIAlertControllerStyleAlert];

    // Present the alert view controller.
    [self presentViewController:alert animated:YES completion:^{

        /// Perform long operation here...

        dispatch_after(0, dispatch_get_main_queue(), ^{

            // Dismiss the alert message.
            [loadAlertController dismissViewControllerAnimated:YES completion:nil];

            /// Execute rest of code INDSIDE the dispatch code block INSTEAD of after it...
        });
     }];
}