如何从UIBarButtonItem有条件地显示UIMenu或弹窗?
解决方案(iOS 15.0+)
要实现点击UIBarButtonItem时先做运行时检查,满足条件才展示UIMenu,否则显示弹窗,你可以通过以下两种可靠方式实现:
方式一:利用底层UIButton触发菜单显示
直接获取UIBarButtonItem对应的底层UIButton实例,调用其showMenu方法手动展示菜单,这是最简洁的实现方式:
// 初始化按钮时绑定action btnAdd = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addAction)) ... @objc func addAction(_ sender: UIBarButtonItem) { if someRuntimeCondition { // 动态创建UIMenu let menu = UIMenu(children: [ UIAction(title: "新建文档", handler: { _ in // 处理新建文档逻辑 }), UIAction(title: "新建文件夹", handler: { _ in // 处理新建文件夹逻辑 }) ]) // 给按钮绑定菜单 sender.menu = menu // 获取底层UIButton并触发菜单显示 if let button = sender.value(forKey: "view") as? UIButton { button.showMenu(from: button, rect: button.bounds, preferredEdge: .bottom) } } else { // 显示条件不满足的弹窗 let alert = UIAlertController(title: "提示", message: "当前无法执行此操作", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } }
说明:value(forKey: "view")用于获取UIBarButtonItem对应的原生UIButton,在iOS 15及以上版本中该属性稳定可用,不会出现兼容性问题。
方式二:使用UIContextMenuInteraction展示菜单
如果不想依赖底层控件的私有属性,可以通过UIContextMenuInteraction手动触发菜单展示,这种方式更符合官方API设计规范:
1. 实现UIContextMenuInteractionDelegate
在你的ViewController中扩展实现代理方法,用于提供菜单内容:
extension YourViewController: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in // 返回需要展示的UIMenu return UIMenu(children: [ UIAction(title: "新建文档", handler: { _ in // 处理逻辑 }), UIAction(title: "新建文件夹", handler: { _ in // 处理逻辑 }) ]) } } }
2. 在action中触发菜单展示
@objc func addAction(_ sender: UIBarButtonItem) { if someRuntimeCondition { guard let buttonView = sender.value(forKey: "view") as? UIView, let _ = buttonView.window else { return } // 创建上下文菜单交互对象 let interaction = UIContextMenuInteraction(delegate: self) buttonView.addInteraction(interaction) // 触发菜单展示 interaction.showMenu(at: buttonView.bounds, in: buttonView, preferredEdge: .bottom) // 菜单展示后移除交互,避免重复添加 DispatchQueue.main.async { buttonView.removeInteraction(interaction) } } else { // 显示弹窗 let alert = UIAlertController(title: "提示", message: "当前无法执行此操作", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } }
说明:这种方式不依赖UIBarButtonItem的menu属性,完全通过上下文交互来控制菜单展示,适合对API调用严谨性有要求的场景。
内容的提问来源于stack exchange,提问作者HangarRash
相关产品推荐
相关产品推荐

