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

macOS状态栏应用窗口置顶时菜单偶尔失效的问题求助

问题

我开发的一款macOS状态栏应用存在偶现问题(无法100%复现):当显示引导窗口(Onboarding)时,窗口处于聚焦状态时菜单正常工作,窗口未聚焦时菜单有时无法显示。移除OnboardingWindowController中设置NSApplication.shared.activate(ignoringOtherApps: true)和window?.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))这两行代码后,问题消失。但我需要引导窗口保持在前台(避免用户在引导流程中打开设置或其他窗口而偏离流程,影响转化),请问如何在保留窗口前台显示的同时修复菜单失效问题?

相关代码

OnboardingWindowController代码

class OnboardingWindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()

        NSApplication.shared.activate(ignoringOtherApps: true)
        window?.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))
    }
}

窗口打开代码

@MainActor
private func showOnboardingWindow() {
    showInitialController(ofStoryboard: "Onboarding", withIdentifier: "OnboardingWindowController")
}

@MainActor
private func showInitialController(
    ofStoryboard storyboardName: String,
    withIdentifier identifier: String
) {
    closeAllWindows()

    DispatchQueue.main.async {
        let storyboard = NSStoryboard(name: storyboardName, bundle: nil)

        guard let windowController = storyboard.instantiateController(withIdentifier: identifier) as? NSWindowController else {
            self.logHandler.log(errorString: "Could not initialize NSWindowController \(identifier) on \(storyboardName)")
            return
        }

        guard let window = windowController.window else {
            self.logHandler.log(errorString: "NSWindowController \(windowController) does not have a window while opening \(identifier) on \(storyboardName)")
            return // TODO: Handle?
        }

        if #available(macOS 14.0, *) {
            NSApp.activate()
        } else {
            NSApp.activate(ignoringOtherApps: true)
        }

        DispatchQueue.main.async {
            windowController.showWindow(self)
            //window.level = NSWindow.Level.init(rawValue: 1)
            window.orderFrontRegardless()
            //window.makeKeyAndOrderFront(nil)
        }
    }
}

@MainActor
private func closeAllWindows() {
    NSApplication.shared.windows.filter { $0.contentViewController != nil }.forEach { window in
        window.close()
    }
}

解决方案

1. 替换窗口层级,避免脱离应用管理

使用NSWindow.Level.floating替代最高层级maximumWindow,这个层级既能保证窗口在绝大多数普通窗口上方,又不会完全脱离应用的窗口逻辑链,避免菜单交互异常:

class OnboardingWindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()
        // 移除原activate调用,统一在打开逻辑中处理
        window?.level = .floating
    }
}

2. 统一激活逻辑,避免重复操作

原代码中OnboardingWindowController和showInitialController都调用了应用激活,重复操作会导致焦点混乱。仅在窗口打开逻辑中处理激活,并确保窗口成为关键窗口:
修改showInitialController中的窗口显示代码段:

// 替换原嵌套的DispatchQueue.main.async代码块
windowController.showWindow(self)
window.level = .floating
window.makeKeyAndOrderFront(nil) // 确保窗口成为关键窗口,绑定应用菜单交互

if #available(macOS 14.0, *) {
    NSApp.activate()
} else {
    NSApp.activate(ignoringOtherApps: true)
}

3. 简化异步调用,避免时序问题

原代码存在多层嵌套的DispatchQueue.main.async,可能导致窗口显示时序混乱,直接移除冗余嵌套:

@MainActor
private func showInitialController(
    ofStoryboard storyboardName: String,
    withIdentifier identifier: String
) {
    closeAllWindows()

    let storyboard = NSStoryboard(name: storyboardName, bundle: nil)
    guard let windowController = storyboard.instantiateController(withIdentifier: identifier) as? NSWindowController else {
        self.logHandler.log(errorString: "Could not initialize NSWindowController \(identifier) on \(storyboardName)")
        return
    }
    guard let window = windowController.window else {
        self.logHandler.log(errorString: "NSWindowController \(windowController) does not have a window while opening \(identifier) on \(storyboardName)")
        return
    }

    windowController.showWindow(self)
    window.level = .floating
    window.makeKeyAndOrderFront(nil)
    
    if #available(macOS 14.0, *) {
        NSApp.activate()
    } else {
        NSApp.activate(ignoringOtherApps: true)
    }
}

4. 可选:窗口失焦时自动抢回焦点

如果需要严格保证引导窗口不被遮挡,可以添加窗口失焦监听,自动重新激活窗口:

class OnboardingWindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()
        window?.level = .floating
        window?.delegate = self
    }
}

extension OnboardingWindowController: NSWindowDelegate {
    func windowDidResignKey(_ notification: Notification) {
        DispatchQueue.main.async {
            self.window?.makeKeyAndOrderFront(nil)
            if #available(macOS 14.0, *) {
                NSApp.activate()
            } else {
                NSApp.activate(ignoringOtherApps: true)
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Lucas van Dongen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 15:35:04