UIAlertController 不会立即显示

Posted

技术标签:

【中文标题】UIAlertController 不会立即显示【英文标题】:UIAlertController does not show immediately 【发布时间】:2016-05-27 17:04:44 【问题描述】:

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 消息中,但无济于事。

过去,在这种情况下,使用带有 dispatch_queue 的 UIAlertView 可以无缝地工作。

感谢任何帮助。

编辑:

需要注意的一件有趣的事情:在 dispatch_async 代码中,如果我在调用长操作代码之前添加一个 usleep(250000),大约 80% 的时间,UIAlertController 警报消息将在正确的时间显示:BEFORE漫长的操作开始了。然而,这不是 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];
    );
);

【问题讨论】:

您的第二个变体是正确的,您应该发布更多关于您的长期操作的代码。您可能在长操作中的某处阻塞了主线程。这会导致这个问题。 @Sulthan 是的,在长操作代码中,我使用 TCP 套接字来 sendrecv 数据。 recv 套接字函数会阻塞,直到它从服务器接收到数据。 你是在后台线程上做的(在第二个版本中),所以这应该不是问题。我猜你也不知何故阻塞了那里的主线程。 【参考方案1】:

最终起作用的是调度代码和 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...
        );
    );

【讨论】:

【参考方案2】:

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

更多关于来自 iOS reference 的完成调用:“完成处理程序在 viewDidAppear 之后被调用:在呈现的视图控制器上调用方法。”

(This post 提供了帮助。)

- (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...
        );
     ];

【讨论】:

【参考方案3】:

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

【讨论】:

感谢您的建议。我只是尝试了这两种情况(有和没有调度),但它没有工作......它表现出相同的行为。 我建议了这个答案,因为您可能不在主线程中,这就是延迟的原因。

以上是关于UIAlertController 不会立即显示的主要内容,如果未能解决你的问题,请参考以下文章

Swift UIAlertController 显示自定义消息

AccessibilityPerformEscape 不会关闭 UIAlertController

UIAlertController:它不会在一个控件中关闭一个UICollectionView

当 UIAlertController 处于活动状态时,VoiceOver Z 手势不会触发

iOS - UIAlertController三种显示提示框代码

在 UIAlertController 的文本字段中选择文本