为啥必须在主队列上异步调用 resignFirstResponder() 来关闭键盘

Posted

技术标签:

【中文标题】为啥必须在主队列上异步调用 resignFirstResponder() 来关闭键盘【英文标题】:Why have to call resignFirstResponder() asynchronously on the main queue to dismiss the keyboard为什么必须在主队列上异步调用 resignFirstResponder() 来关闭键盘 【发布时间】:2020-03-13 03:35:32 【问题描述】:

我使用 UISearchBar 过滤表格视图的数据,并且我希望在搜索栏没有查询文本时关闭键盘(通过点击键盘删除按钮或点击搜索栏清除按钮删除文本)。根据我从 *** 上的其他帖子中了解到的信息,我了解即使您在 textDidChange UISearchBarDelegate 方法中调用 searchBar.resignFirstResponder(),单击搜索栏清除按钮(带有 x 图标的灰色圆形按钮)也会显示键盘。因此,如果您单击搜索栏清除按钮,键盘将被隐藏然后再次闪回,而手动删除文本将隐藏键盘。

唯一的解决方案是调用 DispatchQueue.main.async 代码块中的 resignFirstResponder() 方法。

谁能解释为什么必须在主队列上异步调用 resignFirstResponder() 方法来关闭键盘?为什么直接调用它不起作用?

检查下面的代码:

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) 
        if searchBar.text?.count == 0 
            loadItems()

            //the following code works!
            DispatchQueue.main.async 
                searchBar.resignFirstResponder()
            

//            //this line of code only works when you tap the deleting keyboard button to remove the query text
//            //does not work if you click the searchbar clear button
//            searchBar.resignFirstResponder()
        
    

【问题讨论】:

【参考方案1】:

简单地说,这是因为你不能让搜索栏退出第一响应者,直到它完成与你的交谈。所以你必须等到textDidChange 委托方法退出。这就是异步重新进入主线程所做的事情。

【讨论】:

【参考方案2】:

如果您只是好奇为什么会这样,@matt 是对的。除了他的回答之外,已经有一个 UISearchBarDelegate 方法在您按下取消按钮时触发。所以你可以用这种方法辞退第一响应者。

func searchBarCancelButtonClicked(_ searchBar: UISearchBar)

我认为这可以帮助您实现想要的目标。

【讨论】:

【参考方案3】:

您可以在文本字段委托方法的 endEditing 方法中退出响应者。这绝对可以帮助您解决这个问题,但主要的 UI 更改可能不会在视图中执行。因此,为了更新 UI,我们需要在调度主队列中实现。

【讨论】:

以上是关于为啥必须在主队列上异步调用 resignFirstResponder() 来关闭键盘的主要内容,如果未能解决你的问题,请参考以下文章

GCD - 如何在主线程上等待在主队列上执行的异步回调

为啥 NSManagedObjectContext 队列在主线程上执行?

为啥从并发队列异步调用`DispatchQueue.main.sync`成功但同步失败?

GCD主队列是不是必须在主线程上执行?

同源和异步的区别

Alamofire RequestRetrier,在主操作队列上调用完成块可以吗?