在 UIAlertAction 处理程序中更新 UI

Posted

技术标签:

【中文标题】在 UIAlertAction 处理程序中更新 UI【英文标题】:Update UI in UIAlertAction handler 【发布时间】:2016-07-22 08:51:37 【问题描述】:

您能帮我解决在显示 UIAlertView 实例时更新程序 UI 的问题吗? 情况是这样的:

    我正在按下工具栏的“隐藏按钮”,并且 alertView 正在打开; 在 UIAlertAction(确定按钮)的处理程序中,我有一个代码,我在其中进行了几个操作: 移除按下的工具栏“隐藏按钮”,改为使用活动指示器设置按钮项; 使指标滚动; 只有在前面的步骤之后,下一部分代码才应该开始并且数据模型正在更新,并且因为它通过 NSFetchedResultsControllerDelegate 连接到 tableView,tableView 的数据将自动更新。此步骤可能需要一些时间,因此非常需要异步保持它,并且在处理它时,活动指示器应该滚动; 在活动指示器滚动出现故障后,工具栏按钮项将被删除,并且“隐藏按钮”(在第一步中删除)又回来了。 完成。

当我交换“隐藏按钮”和“活动按钮”时,问题在于更新 UI。

private var hideDataBarButtonItem: UIBarButtonItem?
private var indicatorBarButtonItem = UIBarButtonItem(customView: UIActivityIndicatorView(activityIndicatorStyle: .Gray))

override func viewDidLoad() 
    super.viewDidLoad()
    ...
    hideDataBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: #selector(hideAllLoginData))
    toolbarItems?.insert(hideDataBarButtonItem!, atIndex: 2)

这就是 hideDataBarButtonItem 的操作:

@IBAction func hideAllLoginData(sender: AnyObject) 
    let confirmAlert = UIAlertController(title: "Hide all data?", message: "", preferredStyle: .Alert)

    confirmAlert.addAction( UIAlertAction(title: "OK", style: .Default, handler:  action in
        // remove the clear-button, set the indicator button instead and start indicator rolling
        self.toolbarItems?.removeAtIndex(2)
        self.toolbarItems?.insert(self.indicatorBarButtonItem, atIndex: 2)
        (self.indicatorBarButtonItem.customView as! UIActivityIndicatorView).startAnimating()
        print("button with indicator added")

        sleep(5) // -> CHECK: this moment indicator should be visible and rolling!

        dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) 
            for section in self.resources 
                for resource in section 
                    if resource.defRecord != nil 
                        resource.defRecord = nil
                    
                
            
            print("data cleared")

            dispatch_async(dispatch_get_main_queue()) 
                // remove indicator and set the clear-button back
                print("button with indicator removed")
                (self.indicatorBarButtonItem.customView as! UIActivityIndicatorView).stopAnimating()
                self.toolbarItems?.removeAtIndex(2)
                self.toolbarItems?.insert(self.hideDataBarButtonItem!, atIndex: 2)
            
        
    ) )
    confirmAlert.addAction( UIAlertAction(title: "Cancel", style: .Cancel, handler: nil ) )

    self.presentViewController(confirmAlert, animated: true, completion: nil)

执行结果:

button with indicator added
// -> 5 sec of awaiting, but that's nothing in interface changed!
data cleared
button with indicator removed

如果我不删除指示器按钮,我毕竟可以看到它,但它必须更早出现。我做错了什么? 谢谢!

【问题讨论】:

尝试将你的睡眠从主线程移到你的后台线程。目前你的 sleep 阻塞了主线程,所以 UIKit 不能在它上面做任何事情,它不会在这个时候开始动画。 @PeterK,它有效。我真的可以在 5 秒内观察到滚动并进行任何 UI 操作。今天晚上,我将检查它如何在具有许多记录且没有睡眠功能的真实设备上工作。如果它这么容易解决,你真的是巫师:)我会回应。 所以理论上,如果 sleep(5) 在上面的位置,我会看到活动指示器,以防它是真正的延迟?但5秒后。睡觉?是否可以在 NSFetchedResultsControllerDelegate 方法更新我的表中的行时进行 UI 操作? NSFetchedResultsControllerDelegate 应该异步工作,是的,您可以进行 UI 操作。我不记得了,但如果 NSFetchedResultsControllerDelegate 不能在主线程上工作,你应该在主线程上执行所有 UI 操作。 在第一种情况下,您阻塞了主线程 5 秒,因此任何动画都无法启动,之后您快速执行了一些操作,因此您可能没有注意到任何更改隐藏活动指示器之前的 UI。 【参考方案1】:

问题解决了。 这是因为 NSFetchedResultsController 更新了不在主线程中的 UI。在我的模型更新时,我不得不停用它的委托。然后,在主线程中,我必须再次激活它并更新 tableView 数据。解决方案在这里(查看 cmets):

        dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) 
            // clear the delegate in order to not to allow UI update by fetchedResultsController
            self.resourceModel.nullifyFetchedResultsControllerDelegate()
            for section in self.resources 
                for resource in section 
                    if resource.defRecord != nil 
                        resource.defRecord = nil
                    
                
            
            print("data cleared")

            dispatch_async(dispatch_get_main_queue()) 
                // set the delegate back and update the tableView
                self.resourceModel.setFetchedResultsControllerDelegate(self)
                self.reloadResources()
                self.tableView.reloadData()

                // remove indicator and set the clear-button back
                print("button with indicator removed")
                (self.indicatorBarButtonItem.customView as! UIActivityIndicatorView).stopAnimating()
                self.toolbarItems?.removeAtIndex(2)
                self.toolbarItems?.insert(self.hideDataBarButtonItem!, atIndex: 2)
            
        

PeterK,感谢您的建议!

【讨论】:

您还可以在每个委托的回调中使用 dispatch_async(dispatch_get_main_queue()) 并为每个委托更改更新 UI,从用户的角度来看,它应该更具响应性。

以上是关于在 UIAlertAction 处理程序中更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

如何执行 UIAlertAction 的处理程序?

制作 UIAlertAction 处理程序的正确方法

UIAlertAction 处理程序延迟后运行

UIAlertAction 的处理程序有点太晚了——我怎样才能让它立即生效?

iOS弹窗UIAlertAction用法

UIAlertAction - 处理动作