Swift 3 - 异步显示警报控制器,长功能在后台运行

Posted

技术标签:

【中文标题】Swift 3 - 异步显示警报控制器,长功能在后台运行【英文标题】:Swift 3 - Display alert controller asynchronously with long function running in background 【发布时间】:2017-03-30 20:58:09 【问题描述】:

我正在使用 Swift 3。

我试图做的行为是:用户点击一个按钮,一个旋转的齿轮警报控制器显示,同时它启动一个长时间运行的功能。一旦该函数执行完毕,旋转的齿轮就会消失,视图控制器也会消失。

下面的代码启动doProcessing 函数,但直到视图关闭前大约一秒钟才显示旋转齿轮。所以这不太对。

func displaySpinningGear() 

    print("display spinning gear")
    // show the alert window box
    let activityAlertController = UIAlertController(title: "Processing", message: "Please wait while the photo is being processed.", preferredStyle: .alert)

    //create an activity indicator
    let indicator = UIActivityIndicatorView(frame: activityAlertController.view.bounds)
    indicator.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    indicator.hidesWhenStopped = true
    indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray

    //add the activity indicator as a subview of the alert controller's view
    activityAlertController.view.addSubview(indicator)
    indicator.isUserInteractionEnabled = false // required otherwise if there buttons in the UIAlertController you will not be able to press them
    indicator.startAnimating()

    print("start animating")

    self.present(activityAlertController, animated: true, completion: nil)


func onButtonClick() 
    self.displaySpinningGear()
    DispatchQueue.main.async 
        self.doProcessing() // long running function
    

    if let viewController = presentingViewController 
        // This block will dismiss both current and a view controller presenting current
        viewController.dismiss(animated: true, completion: nil)
    
    else 
        // This block will dismiss only current view controller
        self.dismiss(animated: true, completion: nil)
    

下面的代码启动了doProcessing 函数,但视图立即消失,我可以从控制台得知我的doProcessing 函数仍在运行。这也不对。

function onButtonClick() 
    DispatchQueue.global(qos: .background).async 
        print("Processing")
        self.doProcessing() // run in background

        DispatchQueue.main.async 
            self.displaySpinningGear()
        
    

    if let viewController = presentingViewController 
        // This block will dismiss both current and a view controller presenting current
        viewController.dismiss(animated: true, completion: nil)
    
    else 
        // This block will dismiss only current view controller
        self.dismiss(animated: true, completion: nil)
    

如何在显示旋转齿轮时启动背景功能并在后台功能完成运行时(而不是之前)关闭视图和警报控制器?

编辑

按照@Honey 在评论中的建议,尝试移动代码以在背景块之外旋转齿轮,但无济于事。当进程函数仍在处理时,视图会立即关闭(我可以通过打印语句来判断)。

func onButtonClick() 
    DispatchQueue.main.async 
        self.displaySpinningGear()
    
    DispatchQueue.global(qos: .background).async 
        print("Processing")
        self.doProcessing() // run in background
    

    if let viewController = presentingViewController 
        // This block will dismiss both current and a view controller presenting current
        viewController.dismiss(animated: true, completion: nil)
    
    else 
        // This block will dismiss only current view controller
        self.dismiss(animated: true, completion: nil)
    

【问题讨论】:

在您的第二个解决方案中:尝试将DispatchQueue.main.async self.displaySpinningGear() 删除到DispatchQueue.global(qos: .background).async外部。此外,将您的函数命名为 process 会更好。 @Honey 嗯,这似乎不起作用。请参阅帖子中的编辑。 【参考方案1】:

从长时间运行的函数中进行回调,以便在它结束时返回一个值并捕获它以使警报消失。

试试看:

typealias DoProcessingCallback = (_ finished: Bool) -> Void

func onButtonClick() 
    self.displaySpinningGear()

    self.doProcessing(callback:  (finished) in

        if finished 
           // Here you DismissViewController
           // Here you DismissAlert
        

    ) // long running function




func doProcessing(callback: DoProcessingCallback) 
    // YOUR LONG CODE....
    // When you know it already finished
    callback(true)

希望对你有帮助

【讨论】:

不幸的是,我试过了,但没有成功。行为是单击按钮,不显示警报,因此它只是在处理发生时坐在那里,在处理完成前大约一秒钟,警报显示,然后视图关闭。 尝试在调用self.present(activityAlertController, animated: true, completion: nil)之后调用de doProcessing回调函数 澄清一下:您的意思是将self.doProcessing(callback: ..... 块移动到displaySpinningGear 函数中self.present(activityAlertController...) 行之后?我这样做了,并且发生了同样的行为。 是的,我的意思是...你确定doProcessing函数需要足够的时间吗?可能此功能花费的时间比您想象的要少,并且警报没有时间显示。【参考方案2】:

我遇到了同样的问题并尝试了很多不同的方法,这就是有效的:

activityView.alpha = 0.8
DispatchQueue.global(qos: .default).async(execute: 
    DispatchQueue.main.async(execute: 
        self.performSegue(withIdentifier: "cropToProcessed", sender: self)
    )
)

基本上,我最初将活动指示器的 alpha 设置为 0.0,当按下按钮时,我将其设置为 0.8,然后在 viewWillDisappear 中将其设置回 0.0,它可以工作

【讨论】:

以上是关于Swift 3 - 异步显示警报控制器,长功能在后台运行的主要内容,如果未能解决你的问题,请参考以下文章

电脑警报声

swift 2.2中api(json)没有数据时如何显示警报

我可以为警报控制器添加长按事件吗

在主视图控制器中显示警报视图(从子类调用)

在 Swift 中显示警报视图会导致 EXC BAD ACCESS [重复]

用户在swift中点击firebase通知后如何显示警报?