打开实用工具面板时如何保持NSNormalWindowLevel窗口的焦点
我的应用包含多个处于NSNormalWindowLevel层级的窗口,近期新增了一个设为NSFloatingWindowLevel层级的实用工具面板。该面板能正常以悬浮窗口形式打开并置于其他窗口上方,可通过点击普通窗口或面板切换焦点。但问题是,打开面板时它总会抢占焦点,我希望焦点保留在NSNormalWindowLevel层级的顶层窗口,无需用户点击即可继续操作。
基础代码如下:
storyBoard = [NSStoryboard storyboardWithName:@"UtilityPanel" bundle:nil]; utilityPanelController = [storyBoard instantiateControllerWithIdentifier:@"Utility Panel"]; [utilityPanelController showWindow:sender];
我尝试通过获取顶层窗口并调用orderFront:来强制其前置:
NSWindow *topWindow = [[NSApplication sharedApplication] keyWindow]; [topWindow orderFront:nil];
但面板仍保持焦点,尝试orderFrontRegardless也得到相同结果。我推测这两个方法仅对同层级窗口生效,而当前窗口处于不同层级。我查阅了窗口管理器,但尚未找到在打开面板时通过代码将焦点切换到特定窗口层级(如NSNormalWindowLevel)的方法,是否遗漏了什么?
核心是阻止浮动面板主动抢占焦点,同时确保原有普通窗口的焦点状态不受影响,以下是几种可行方案:
方案一:利用NSPanel专属属性(推荐)
如果你的实用工具面板是NSPanel子类,直接设置becomesKeyOnlyIfNeeded属性为YES即可。这是系统为面板类提供的专门用于避免自动抢焦点的配置:
// 在面板窗口控制器的windowDidLoad方法中添加 ((NSPanel *)self.window).becomesKeyOnlyIfNeeded = YES;
开启后,面板只有在用户主动点击时才会成为关键窗口,打开时不会抢占原有窗口的焦点。
方案二:修改窗口的焦点权限
如果不是NSPanel,可通过设置窗口的canBecomeKeyWindow属性,阻止它成为关键窗口:
// 在面板窗口初始化时添加 self.window.canBecomeKeyWindow = NO; self.window.ignoresMouseEvents = NO; // 保持面板可响应鼠标操作,仅关闭焦点抢占
该设置下,面板始终无法成为关键窗口,原有普通窗口的焦点会自动保留。
方案三:显示面板后手动恢复焦点
如果不想修改面板的窗口属性,可在显示面板前记录当前关键窗口,显示后强制其重新获得焦点:
// 记录当前的关键窗口 NSWindow *originalKeyWindow = [[NSApplication sharedApplication] keyWindow]; // 加载并显示面板 storyBoard = [NSStoryboard storyboardWithName:@"UtilityPanel" bundle:nil]; utilityPanelController = [storyBoard instantiateControllerWithIdentifier:@"Utility Panel"]; [utilityPanelController showWindow:sender]; // 强制原窗口重新成为关键窗口 [originalKeyWindow makeKeyWindow];
makeKeyWindow不受窗口层级限制,能直接将指定窗口设为焦点窗口,覆盖面板的抢占行为。
内容的提问来源于stack exchange,提问作者johnpurlia

