macOS监听和响应NSWindow的协议NSWindowDelegate
macOS监听和响应NSWindow的协议NSWindowDelegate

macOS监听和响应NSWindow的协议NSWindowDelegate

NSWindowDelegate 是 macOS 中的一个协议(protocol),用于监听和响应 NSWindow 的生命周期事件和用户操作,例如:

1、窗口关闭;

2、最小化 / 恢复;

3、移动 / 改变大小;

4、激活 / 取消激活;

5、变成主窗口 / 辅助窗口。

总的来说,NSWindowDelegate 就是 macOS AppKit 中专门用来“监听窗口事件”的代理协议。

就像 UIViewController 有 viewDidLoad,UITableView 有 UITableViewDelegate,

NSWindow 的各种行为和生命周期事件也都由 NSWindowDelegate 控制。

基本用法

class MyWindowDelegate: NSObject, NSWindowDelegate {
    
    // 关闭窗口前,是否允许关闭?
    func windowShouldClose(_ sender: NSWindow) -> Bool {
        print("请求关闭窗口")
        return true // 返回 false 可以拦截关闭
    }
    
    // 窗口将关闭
    func windowWillClose(_ notification: Notification) {
        print("窗口将关闭")
    }
    
    // 窗口尺寸发生变化
    func windowDidResize(_ notification: Notification) {
        print("窗口尺寸变了")
    }
    
    // 窗口位置发生变化
    func windowDidMove(_ notification: Notification) {
        print("窗口移动了")
    }
}

设置代理:

let window = NSWindow(...)
window.delegate = myWindowDelegate

需要将某个对象(通常是控制器)设置为窗口的 delegate。

通常做法

让 NSWindowController 自己实现代理:

class SettingsWindowController: NSWindowController, NSWindowDelegate {
    override func windowDidLoad() {
        super.windowDidLoad()
        window?.delegate = self
    }

    func windowWillClose(_ notification: Notification) {
        print("设置窗口关闭了")
    }
}

常用方法

1、windowShouldClose:返回是否允许关闭(可以弹出确认框)。

2、windowWillClose:正在关闭,通常用于释放资源。

3、windowDidBecomeMain / ResignMain:是否为主窗口(响应键盘等)。

4、windowDidResize / DidMove:监听窗口大小或位置变化。

5、windowDidMiniaturize / DidDeminiaturize:最小化 / 还原。

6、windowDidEnterFullScreen / DidExitFullScreen:进入 / 离开全屏。

7、windowDidChangeBackingProperties:屏幕缩放比例改变(Retina 相关)。

使用场景

1、关闭前弹出提示:windowShouldClose → 弹出 NSAlert。

2、关闭后清理内存 / 状态:windowWillClose。

3、限制窗口最小尺寸:windowWillResize(to:)。

4、响应窗口尺寸变化更新界面:windowDidResize。

5、多窗口之间切换行为控制:windowDidBecomeMain / ResignMain。

相关文章

macOS管理窗口的控制器类NSWindowController:https://fangjunyu.com/2025/06/30/macos%e7%ae%a1%e7%90%86%e7%aa%97%e5%8f%a3%e7%9a%84%e6%8e%a7%e5%88%b6%e5%99%a8%e7%b1%bbnswindowcontroller/

扩展知识

NSWindowDelegate和NSWindowController

NSWindowController 是控制器,用于管理窗口的创建、显示、关闭等“宏观控制”;

NSWindowDelegate 是代理,用于监听和响应窗口的各种“细节事件”。

两者职责不同、互为补充,不冲突也不重复。

两者区别

1、类型:NSWindowController是类(控制器),NSWindowDelegate是协议(代理)。

2、管理对象:NSWindowController管理一个 NSWindow 实例,NSWindowDelegate监听 NSWindow 的行为和状态。

3、系统自动调用:NSWindowController由系统创建窗口时调用其生命周期方法,NSWindowDelegate只有设置为 window.delegate 后才触发。

4、生命周期方法:NSWindowController有windowDidLoad(),NSWindowDelegate有windowWillClose()、windowDidResize() 等。

5、是否必须有:NSWindowController不是,窗口可以不挂控制器,NSWindowDelegate也不是,窗口可以没有代理。

6、是否可以共用:NSWindowController可以由同一个类实现,推荐 NSWindowController 同时作为代理。

两者共用
class SettingsWindowController: NSWindowController, NSWindowDelegate {

    override func windowDidLoad() {
        super.windowDidLoad()
        window?.delegate = self
        
        print("窗口创建完成,可用于初始化子视图")
    }

    func windowShouldClose(_ sender: NSWindow) -> Bool {
        print("用户尝试关闭窗口,是否允许?")
        return true
    }

    func windowWillClose(_ notification: Notification) {
        print("窗口即将关闭,可以清理资源")
    }

    func windowDidResize(_ notification: Notification) {
        print("窗口大小变了,可调整布局")
    }
}

代码解析:

1、windowDidLoad():属于NSWindowController,用于窗口加载完成,做初始化。

2、windowShouldClose:属于NSWindowDelegate,用于拦截关闭事件。

3、windowWillClose:属于NSWindowDelegate,用于释放资源、保存状态。

4、windowDidResize:属于NSWindowDelegate,用于响应尺寸变化调整界面。

为什么两个都需要?

1、单一职责原则:控制器负责生命周期管理,代理负责事件监听。

2、解耦设计:同一个窗口可以换不同的代理,无需修改控制器。

3、更灵活:比如用一个代理类统一处理多个窗口的关闭行为。

4、SwiftUI 混合:SwiftUI 中窗口不一定有控制器,但可以绑定代理。

5、可复用性:多窗口共享一个代理逻辑,如日志记录、布局适配等。

实际开发建议

1、简单窗口:控制器和代理写在同一个类(NSWindowController & NSWindowDelegate)。

2、想解耦关闭逻辑 / 样式统一:单独写一个代理类来处理多个窗口的行为。

3、SwiftUI 创建窗口时无控制器:设置 window.delegate = someObject 实现生命周期监听。

4、只关注初始化逻辑:只实现 windowDidLoad 即可。

NSWindowController和NSWindowDelegate总结

NSWindowController 管大事(创建、展示、生命周期),

NSWindowDelegate 管小事(拖拽、关闭、激活、尺寸等行为)

一起配合用,职责清晰、不冲突。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注