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

如何检测OS X系统中任意应用进入或退出全屏状态?

在Swift中监听macOS任意应用的全屏状态变化

我完全懂这种踩坑的感觉——macOS的全屏状态监听确实藏得有点深,尤其是用Swift开发时,Objective-C的老代码转过来经常遇到各种适配问题。下面给你一套可行的Swift实现方案,亲测能搞定任意应用的全屏进入/退出通知:

核心思路

利用NSWorkspace监听应用激活/启动/退出事件,再给目标应用的窗口注册NSWindow的全屏状态通知,这样就能精准捕获每个应用的全屏变化。相比轮询窗口信息的方案,这种基于通知的方式更高效、更优雅。

完整Swift代码实现

import Cocoa

class FullScreenMonitor {
    // 存储所有通知监听器,避免被提前释放
    private var observers = [NSObjectProtocol]()
    
    init() {
        setupApplicationObservers()
        // 给当前已激活的前台应用提前注册窗口监听
        if let currentApp = NSWorkspace.shared.frontmostApplication {
            registerWindowNotifications(for: currentApp)
        }
    }
    
    private func setupApplicationObservers() {
        // 监听应用激活事件
        let activateObserver = NSWorkspace.shared.notificationCenter.addObserver(
            forName: NSWorkspace.didActivateApplicationNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else { return }
            self?.registerWindowNotifications(for: app)
        }
        observers.append(activateObserver)
        
        // 监听应用启动事件,处理刚启动就全屏的应用
        let launchObserver = NSWorkspace.shared.notificationCenter.addObserver(
            forName: NSWorkspace.didLaunchApplicationNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else { return }
            self?.registerWindowNotifications(for: app)
        }
        observers.append(launchObserver)
        
        // 监听应用退出事件,清理对应的监听器
        let terminateObserver = NSWorkspace.shared.notificationCenter.addObserver(
            forName: NSWorkspace.didTerminateApplicationNotification,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else { return }
            self?.cleanupWindowNotifications(for: app)
        }
        observers.append(terminateObserver)
    }
    
    private func registerWindowNotifications(for app: NSRunningApplication) {
        // 遍历应用的所有窗口,确保不会漏过任何一个可能全屏的窗口
        guard let windows = app.windows else { return }
        for window in windows {
            addFullScreenObservers(to: window)
        }
    }
    
    private func addFullScreenObservers(to window: NSWindow) {
        // 先移除旧的监听器,避免重复注册
        NotificationCenter.default.removeObserver(
            self,
            name: NSWindow.didEnterFullScreenNotification,
            object: window
        )
        NotificationCenter.default.removeObserver(
            self,
            name: NSWindow.didExitFullScreenNotification,
            object: window
        )
        
        // 注册进入全屏通知
        let enterObserver = NotificationCenter.default.addObserver(
            forName: NSWindow.didEnterFullScreenNotification,
            object: window,
            queue: .main
        ) { notification in
            guard let window = notification.object as? NSWindow,
                  let app = NSWorkspace.shared.application(forWindow: window) else { return }
            let appName = app.localizedName ?? "未知应用"
            print("✅ \(appName) 进入全屏状态")
            // 在这里添加你的业务逻辑,比如发送自定义通知、更新UI等
        }
        observers.append(enterObserver)
        
        // 注册退出全屏通知
        let exitObserver = NotificationCenter.default.addObserver(
            forName: NSWindow.didExitFullScreenNotification,
            object: window,
            queue: .main
        ) { notification in
            guard let window = notification.object as? NSWindow,
                  let app = NSWorkspace.shared.application(forWindow: window) else { return }
            let appName = app.localizedName ?? "未知应用"
            print("❌ \(appName) 退出全屏状态")
            // 在这里添加你的业务逻辑
        }
        observers.append(exitObserver)
    }
    
    private func cleanupWindowNotifications(for app: NSRunningApplication) {
        guard let windows = app.windows else { return }
        for window in windows {
            NotificationCenter.default.removeObserver(
                self,
                name: NSWindow.didEnterFullScreenNotification,
                object: window
            )
            NotificationCenter.default.removeObserver(
                self,
                name: NSWindow.didExitFullScreenNotification,
                object: window
            )
        }
    }
    
    deinit {
        // 销毁时移除所有监听器,避免内存泄漏
        observers.forEach { NotificationCenter.default.removeObserver($0) }
    }
}

关键配置说明

在macOS 10.14及以上系统,你的应用需要申请辅助功能权限才能获取其他应用的窗口信息。请在项目的Info.plist中添加以下键值对:

<key>NSAccessibilityUsageDescription</key>
<string>需要获取应用窗口状态,以监听全屏变化</string>

用户首次启动应用时,系统会弹出权限申请弹窗,务必引导用户授权。

使用方式

在你的应用初始化代码中(比如AppDelegate的applicationDidFinishLaunching方法),创建监听器实例即可:

class AppDelegate: NSObject, NSApplicationDelegate {
    private var fullScreenMonitor: FullScreenMonitor?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        fullScreenMonitor = FullScreenMonitor()
    }
}

补充说明

  • 这套方案覆盖了绝大多数原生macOS应用,包括多窗口应用;
  • 对于Electron等非原生框架开发的应用,部分窗口可能无法被直接捕获,此时可以结合定期扫描CGWindowListCopyWindowInfo的方式补充监听;
  • 如果你之前尝试的Objective-C代码是基于轮询的,建议替换成这套通知方案,减少系统资源消耗。

内容的提问来源于stack exchange,提问作者Kristian Erik Tigersjäl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:21:30