You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现点击Popover内按钮打开新视图控制器时关闭该Popover

解决点击按钮时同时关闭Popover并打开新ViewController的问题

我来帮你搞定这个问题~你当前代码里的核心问题是:直接调用 AppDelegate().popOver.performClose(sender) 会创建一个全新的AppDelegate实例,而不是当前正在运行的那个,所以这个操作根本碰不到已经显示的Popover,自然关不掉它。

下面给你三种可行的解决方案,你可以根据自己的代码结构选择:

方案一:用通知中心跨组件通信

这种方式适合组件之间耦合度低的场景,通过发送通知让AppDelegate主动关闭Popover:

  1. 先在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
        )
    }
}
  1. 然后在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
        )
    }
}

方案二:用代理模式明确通信关系

如果希望组件之间的依赖关系更清晰,推荐用代理模式:

  1. 先定义一个代理协议:
protocol PopoverCloseDelegate: AnyObject {
    func closePopover()
}
  1. 在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)
    }
}
  1. 最后在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:36:57