NSWindowController 是 macOS 应用开发中(AppKit 框架里)用于管理窗口(NSWindow)的控制器类。
class NSWindowController : NSResponder
NSWindowController 是 macOS 上管理窗口(NSWindow)生命周期、内容视图、显示/关闭逻辑的对象,类似于 iOS 中的 UIViewController 管理一个 UIView。
基本结构
一个标准的 macOS 窗口由四个层次构成:
NSWindowController → NSWindow → NSViewController → NSView
NSWindowController:管理窗口(是否显示、关闭、设置内容控制器等)
NSWindow:窗口本体(有标题栏、边框)
NSViewController:窗口的内容控制器(页面逻辑)
NSView:实际绘制 UI 的视图
常见方法和属性
1、初始化方法
init(window: NSWindow?):用已有的 NSWindow 初始化窗口控制器。
init(windowNibName: NSNib.Name):通过 nib 文件初始化窗口(Interface Builder 中常用)。
2、显示/关闭/管理窗口
showWindow(_ sender: Any?):显示窗口,常用于用户点击后弹出窗口。
close():关闭窗口。
window:当前管理的 NSWindow 对象。
isWindowLoaded:当前窗口是否已经加载(windowDidLoad() 是否已调用)。
loadWindow():手动加载窗口(一般用于 nib 创建时)。
3、生命周期
windowDidLoad():当窗口加载完成后调用(用于初始化视图)。
windowWillLoad():在窗口即将加载前调用(较少使用)。
windowShouldClose(_:):控制窗口是否能被关闭,返回 true/false。
windowWillClose(_:):一般通过通知监听关闭事件,而不是重写这个方法。
4、设置内容控制器
contentViewController:设置或获取窗口中的主视图控制器(NSViewController)。
windowFrameAutosaveName:自动保存/恢复窗口尺寸和位置的标识名(需开启)。
5、管理窗口层级 / 其他行为
shouldCascadeWindows:是否自动级联(错开)窗口,默认 true。
owner:可选,设定拥有者对象(用得较少)。
synchronizeWindowTitleWithDocumentName:是否同步窗口标题与文档名称。
document:如果是文档窗口,可关联 NSDocument。
6、监听窗口关闭通知
NotificationCenter.default.addObserver(
self,
selector: #selector(windowWillClose(_:)),
name: NSWindow.willCloseNotification,
object: self.window
)
然后实现:
@objc func windowWillClose(_ notification: Notification) {
// 清理资源、释放窗口控制器
}
注意:窗口关闭后,如果没有对 NSWindowController 保留强引用,它可能被释放。
示例代码
在菜单栏 app 或主应用里点击“设置”,想要打开一个专属的、可复用的设置窗口:
1、定义 SwiftUI 设置视图
struct SettingsView: View {
var body: some View {
VStack {
Text("偏好设置")
Toggle("开启某项功能", isOn: .constant(true))
}
.padding()
.frame(width: 300, height: 200)
}
}
2、创建 NSWindowController
import AppKit
import SwiftUI
class SettingsWindowController: NSWindowController {
init() {
let hosting = NSHostingController(rootView: SettingsView())
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 300, height: 200),
styleMask: [.titled, .closable, . resizable],
backing: .buffered,
defer: false
)
window.contentViewController = hosting
window.title = "设置"
window.center()
super.init(window: window)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
3、点击按钮弹出这个设置窗口
struct ContentView: View {
@State private var settingsWindow: SettingsWindowController?
var body: some View {
Button("打开设置") {
if settingsWindow == nil {
settingsWindow = SettingsWindowController()
}
settingsWindow?.showWindow(nil)
NSApp.activate(ignoringOtherApps: true) // 保证窗口跳到前台
}
}
}
注意:这里用 @State 保存 NSWindowController 实例,是为了避免窗口关闭后对象被销毁,再次点击按钮时还能复用它。

代码详解
1、创建自定义窗口控制器
class CustomWindowController: NSWindowController {
创建一个自定义的窗口控制器,继承自 NSWindowController
这是专门用来管理一个窗口及其视图内容的类。可以调用 .showWindow() 来显示这个窗口。
2、重写初始化方法
init() {
重写初始化方法,表示要以“代码方式”初始化窗口(不是通过 XIB 或 Storyboard)。
3、使用NSHostingController封装SwiftUI的视图,以便在AppKit中访问SwiftUI视图。
let hosting = NSHostingController(rootView: contentView)
4、创建NSWindow实例
let window = NSWindow(
contentRect: NSRect(x: 100, y: 100, width: 800, height: 600),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false
)
contentRect:初始窗口的位置和大小,(x: 100, y: 100, width: 800, height: 600)。
styleMask:窗口的样式,这里是“有标题栏 + 可关闭 + 可调整大小”。
backing:使用 buffered 表示双缓冲绘图。
defer:是否推迟窗口创建,通常用 false 即可。
5、NSWindow配置
window.contentViewController = hosting
设置窗口的内容控制器为NSHostingController包装过的 SwiftUI View。
window.title = "设置"
设置窗口的标题栏标题。
window.center()
把窗口居中显示(忽略 contentRect 里的 x, y)。
6、调用父类init构造函数
super.init(window: window)
调用父类的 init(window:) 构造函数,传入刚刚创建好的 NSWindow。
这一步是 NSWindowController 正确绑定窗口的关键步骤。
7、required构造函数
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
因为没有通过 XIB / Storyboard 初始化,所以不需要实现这个构造器。
通常只保留 fatalError 即可,避免 Xcode 报错。
注意:contentRect设置的NSRect并不会影响到窗口的最终尺寸。因为设置了:
window.contentViewController = NSHostingController(rootView: SettingsView())
当把一个 SwiftUI 视图封装到NSHostingController后,它内部其实是 AppKit 的 NSViewController,并且它的视图尺寸是由 SwiftUI 的 SettingsView() 布局决定的。
NSWindowController和SwiftUI区别
NSWindowController可以实现:
1、精确设置窗口初始大小:setFrame(…)。
2、控制窗口打开位置:.center(), .setFrameOrigin(…)。
3、控制窗口风格(无标题栏、透明、浮动窗口等):styleMask、level = .floating。
4、保持窗口一直在最前:window.level = .floating。
5、禁用窗口关闭按钮或拖动:standardWindowButton, isMovable。
6、自定义关闭行为(确认、拦截):windowShouldClose()。
7、多个独立窗口控制(每个窗口保活):(SwiftUI窗口关闭后失效),使用多个 NSWindowController 实例。
8、精准窗口销毁控制:windowWillCloseNotification, deinit。
9、窗口恢复上次大小位置:windowFrameAutosaveName。
SwiftUI可以实现:
1、声明式 UI 布局,NSWindowController需手写布局代码或 XIB。
2、视图状态响应(@State, @Binding)。
3、简洁 UI 组合,NSWindowController 冗长。
4、动画、导航栈、环境传递。
5、与 iOS 代码共享视图。
补充技巧
1、设置窗口尺寸、位置:window?.setFrame(…) 或初始 frame。
2、保持窗口打开时不退出 App:applicationShouldTerminateAfterLastWindowClosed 返回 false。
3、在 SwiftUI 中调用:MyWindowController().showWindow(nil)。
总结
NSWindowController 是 macOS 专用的窗口管理控制器。
它管理 NSWindow 的生命周期、显示状态、内容控制器等。
对于使用 AppKit、Storyboard/XIB 或桥接 SwiftUI 的高级用法非常重要。
相关文章
macOS显示SwiftUI的桥接控制器NSHostingController:https://fangjunyu.com/2025/06/30/macos%e6%98%be%e7%a4%baswiftui%e7%9a%84%e6%a1%a5%e6%8e%a5%e6%8e%a7%e5%88%b6%e5%99%a8nshostingcontroller/
扩展知识
NSWindowController和NSWindow的关系
NSWindowController ≠ 必须绑定 NSWindow,但它的核心功能就是为了管理 NSWindow。
通常情况下,创建 NSWindowController 的目的,就是为了控制 NSWindow 的生命周期和行为。
NSWindow是一个实际的窗口对象,NSWindowController是一个控制器,负责管理这个窗口的打开、关闭、行为逻辑。
NSWindow窗口对象可以不需要控制器,但NSWindowController控制器必须管理窗口才有意义。
NSWindow通常被NSWindowController管理,而NSWindowController通常需要持有一个window。
只用 NSWindow,不用 NSWindowController
let window = NSWindow(...)
window.makeKeyAndOrderFront(nil)
适用于:非常简单的窗口,比如:
浮动工具窗口。
调试窗口。
全屏展示图像。
弹出透明窗口。
需要手动管理其打开和关闭,不会自动释放、不会走 delegate 生命周期。
用 NSWindowController
let controller = MyWindowController()
controller.showWindow(nil)
这个时候:
MyWindowController 内部持有一个 NSWindow
可以复用控制器(防止窗口销毁)。
可以走 windowDidLoad() 生命周期。
可以响应窗口关闭、最小化等行为。
更易管理多个窗口(特别是文档、多窗口结构)。
推荐作为标准窗口管理方式。也可以绑定 XIB(传统)或手动创建窗口(现代)。