如何实现点击Popover内按钮打开新视图控制器时关闭该Popover
解决点击按钮时同时关闭Popover并打开新ViewController的问题
我来帮你搞定这个问题~你当前代码里的核心问题是:直接调用 AppDelegate().popOver.performClose(sender) 会创建一个全新的AppDelegate实例,而不是当前正在运行的那个,所以这个操作根本碰不到已经显示的Popover,自然关不掉它。
下面给你三种可行的解决方案,你可以根据自己的代码结构选择:
方案一:用通知中心跨组件通信
这种方式适合组件之间耦合度低的场景,通过发送通知让AppDelegate主动关闭Popover:
- 先在AppDelegate里添加通知监听:
import Cocoa @main class AppDelegate: NSObject, NSApplicationDelegate { var popOver: NSPopover! func applicationDidFinishLaunching(_ aNotification: Notification) { // 这里保留你初始化Popover的原有代码 // 添加通知监听,收到通知就关闭Popover NotificationCenter.default.addObserver( self, selector: #selector(closePopover), name: NSNotification.Name("ClosePopoverNotification"), object: nil ) } @objc private func closePopover() { popOver.performClose(nil) } func applicationWillTerminate(_ aNotification: Notification) { // 程序退出前移除监听,避免内存泄漏 NotificationCenter.default.removeObserver( self, name: NSNotification.Name("ClosePopoverNotification"), object: nil ) } }
- 然后在PopOverViewController的按钮点击方法里发送通知:
class PopOverViewController: NSViewController { @IBAction func showViewController(sender: Any) { // 发送关闭Popover的通知 NotificationCenter.default.post( name: NSNotification.Name("ClosePopoverNotification"), object: nil ) // 执行segue打开新的ViewController performSegue( withIdentifier: NSStoryboardSegue.Identifier("YourSegueID"), sender: sender ) } }
方案二:用代理模式明确通信关系
如果希望组件之间的依赖关系更清晰,推荐用代理模式:
- 先定义一个代理协议:
protocol PopoverCloseDelegate: AnyObject { func closePopover() }
- 在AppDelegate里实现这个协议,并给Popover的ViewController设置代理:
class AppDelegate: NSObject, NSApplicationDelegate, PopoverCloseDelegate { var popOver: NSPopover! func applicationDidFinishLaunching(_ aNotification: Notification) { let storyboard = NSStoryboard(name: "Main", bundle: nil) // 初始化Popover的ViewController guard let popoverVC = storyboard.instantiateController( withIdentifier: "PopOverViewController" ) as? PopOverViewController else { return } popOver = NSPopover() popOver.contentViewController = popoverVC // 设置代理 popoverVC.delegate = self } // 实现代理方法,关闭Popover func closePopover() { popOver.performClose(nil) } }
- 最后在PopOverViewController里调用代理方法:
class PopOverViewController: NSViewController { // 用weak避免循环引用 weak var delegate: PopoverCloseDelegate? @IBAction func showViewController(sender: Any) { // 通知代理关闭Popover delegate?.closePopover() // 执行segue打开新ViewController performSegue( withIdentifier: NSStoryboardSegue.Identifier("YourSegueID"), sender: sender ) } }
方案三:直接获取当前AppDelegate实例(简单但耦合高)
如果你的代码结构比较简单,也可以直接拿到当前运行的AppDelegate实例来操作:
class PopOverViewController: NSViewController { @IBAction func showViewController(sender: Any) { // 获取当前的AppDelegate实例 if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.popOver.performClose(sender) } // 执行segue打开新ViewController performSegue( withIdentifier: NSStoryboardSegue.Identifier("YourSegueID"), sender: sender ) } }
这种方式最直接,但会让PopoverViewController和AppDelegate耦合在一起,后续代码维护可能会麻烦,所以更推荐前两种解耦的方案。
内容的提问来源于stack exchange,提问作者Jared Pieniazek
相关产品推荐
相关产品推荐

