如何实现从UIToolbar弹出Popover时,禁止点击外部UI且点击外部可关闭它?
解决Popover显示时禁止外部点击+点击外部关闭的问题
你完全不用手动去禁用外部视图,iOS的UIPopoverPresentationController已经提供了更优雅的原生方案来同时满足你的两个需求:
- 禁止用户点击Popover以外的UI元素
- 点击Popover外部自动关闭它
核心思路:利用passthroughViews属性
passthroughViews是UIPopoverPresentationController的一个属性,它定义了在Popover显示期间仍然可以响应用户点击的视图集合。默认情况下,这个集合会包含触发Popover的UIBarButtonItem以及工具栏上的其他按钮,这就是为什么你现在还能点其他Toolbar按钮的原因。
只要把这个属性设为空数组,就能让Popover以外的所有视图都无法接收点击事件,同时iOS原生的"点击外部关闭Popover"的行为会保持生效。
完整代码示例
结合你给出的代码片段,修改后的实现如下:
// 准备操作菜单 let storyboard = UIStoryboard(name: "Main", bundle: nil) guard let actionsVC = storyboard.instantiateViewController(withIdentifier: "ActionsViewController") as? ActionsViewController else { return } // 设置Popover的展示样式 actionsVC.modalPresentationStyle = .popover // 获取Popover展示控制器(必须确保modalPresentationStyle是popover才能拿到) guard let popoverPresenter = actionsVC.popoverPresentationController else { return } // 将Popover关联到触发它的UIBarButtonItem popoverPresenter.barButtonItem = yourTriggerBarButtonItem // 替换成你实际的UIBarButtonItem对象 // 关键设置:禁止所有外部视图的点击响应 popoverPresenter.passthroughViews = [] // (可选)设置代理,监听Popover的消失事件(如果需要做后续处理) popoverPresenter.delegate = self // 弹出Popover present(actionsVC, animated: true, completion: nil)
如果需要监听Popover的消失状态,可以实现UIPopoverPresentationControllerDelegate协议:
extension YourViewController: UIPopoverPresentationControllerDelegate { // Popover被关闭时触发(包括点击外部关闭) func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) { // 这里可以添加Popover关闭后的逻辑,比如恢复某些状态 } // (可选)控制是否允许点击外部关闭Popover,默认返回true即可 func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool { return true } }
为什么不推荐手动禁用外部视图?
手动遍历禁用Toolbar或其他外部视图的话,后续还需要手动恢复它们的交互状态,不仅代码繁琐,还容易出现遗漏(比如忘记恢复某个视图的isUserInteractionEnabled)。而使用passthroughViews是系统原生支持的方案,既简洁又能保证行为符合iOS的交互规范。
内容的提问来源于stack exchange,提问作者Dillon Fagan
相关产品推荐
相关产品推荐

