问题 Snow Leopard&LSUIElement - >应用程序无法正常激活,窗口不是“活动”,尽管是“关键”


我遇到了一个问题,后台应用程序使用LSUIElement = 1隐藏其停靠项,菜单栏并阻止它出现在Command-Tab应用程序切换器中。

它似乎只是雪豹的问题。

应用程序在菜单栏中放置一个NSStatusItem,并在单击时弹出一个菜单。选择“首选项...”应该会显示带有首选项的NSWindow。

看起来不起作用的第一件事是Window没有在前面订购,而是出现在所有其他应用程序窗口后面。

我试着通过打电话来解决这个问题

[[NSApplication sharedApplication] activateIgnoringOtherApps: YES]

但那没用。

过了一会儿,我发现菜单阻止了发送到运行循环的消息,所以我在MainController上写了另一个方法并发送了一个延迟的消息:

[self performSelector:@selector(setFront :)               withObject:[preferencesController window] afterDelay:1.0];

-(void)setFront: (id) theWindow {

 [[NSApplication sharedApplication]activateIgnoringOtherApps:YES];
 [theWindow orderFrontRegardless];
 [theWindow makeKeyWindow]; 
        [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}

请注意send-every-possible-message-to-make-it-do-it-it-it-doing-doing方法。

这种方法很有效,窗口在所有应用程序的所有其他窗口的顶部显示,但大部分时间它都不活动,这意味着它的标题栏显示为灰色。单击标题栏也不会使窗口处于活动状态。单击窗口的内部将激活它!?

这一切在Leopard中似乎都不是问题;只是调用activateIgnoringOtherApps并使窗口键似乎工作得很好。

在Snow Leopard中有一个新的API,用于替换应该模拟其行为的LSUIElement:

http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html

我已经玩过了,但它只是SL,我无法设置LSUIElement。


9296
2018-05-28 11:52


起源

你想要做的事情可以被视为窃取焦点,这是相当困难的。这是件好事。你的菜单有什么作用? [preferencesController showWindow:]? - zneak
当用户选择“首选项...”并且您将首选项窗口排在前面并将其设为关键时,它几乎不会窃取焦点,但是苹果的好人可能会试图阻止您这样做。 - Frank R.


答案:


这很奇怪 - 我正在Snow Leopard下写一个LSUIElement应用程序,我没有你所描述的问题......我确实遇到了新创建的窗口没有出现在前面的问题,但是我通过调用activateIgnoringOtherApps修复它。这就是我必须要做的就是让它按原样运作:

[NSApp activateIgnoringOtherApps: YES];
[preferencesWindow makeKeyAndOrderFront: self];

我甚至没有触及名字中有“政策”的任何东西。


8
2017-11-02 19:49



我认为我所经历的是激活政策与其他东西之间的互动。几周前我的程序中删除了用于选择激活策略的额外代码,没有任何不良影响?这是那些不起作用的事情之一然后突然开始再次正常工作(它当然可能是OS X点更新之一)。也许我正在使用的窗口不能成为关键或主要的!? - Frank R.
这对我有用。我有同样的情况。但我的问题有点怪异。如果我从XCode运行它,它完美地工作。如果我从Finder运行应用程序,则不会出现窗口。添加这一行有助于我真的很开心。 - Vojto
它给了我很多麻烦。感谢这个优雅的解决方案 - Tibidabo


在绝望中发布问题之后,我确实继续寻找并最终找到了解决方案。由于这让我困扰了几天,谷歌似乎找不到其他答案,我将解释“后代”的解决方案。

Snow Leopard添加了一个新的NSApplication presentationOptions API:

http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html

这应该模拟LSUIElement的工作方式,但提供更多的开发人员控制。不幸的是,模拟并不完美,因此10.5和10.6之间的行为发生了变化。

特别是,如果您的应用程序在其info.plist中具有LSUIElement = 1行,则Snow Leopard将“将应用程序的presentationOptions ..初始化为NSApplicationPresentationOptions标志的等效组合”。

只有它不是真的。它将新的NSApplication setActivationPolicy设置为NSApplicationActivationPolicyAccessory:

“应用程序没有出现在Dock中,也没有菜单栏,但可以通过编程方式或通过单击其中一个窗口来激活它。这对应于应用程序的Info.plist中的LSUIElement键值为1。”

尽管提到以编程方式激活,但activateIgnoringOtherApps:完全被忽略。

解决方案是将激活策略设置为“常规”:

[[NSApplication sharedApplication] setActivationPolicy: NSApplicationActivationPolicyRegular];

当然,如果你使用10.6 SDK作为Base SDK,那么你只能这样做,这是目前很少有人想做的事情,所以下面是10.5安全的方法:

NSApplication* app = [NSApplication sharedApplication];

if( [app respondsToSelector: @selector(setActivationPolicy:)] ) {

    NSMethodSignature* method = [[app class] instanceMethodSignatureForSelector: @selector(setActivationPolicy:)];
    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: method];
    [invocation setTarget: app];
    [invocation setSelector: @selector(setActivationPolicy:)];
    NSInteger myNSApplicationActivationPolicyAccessory = 0;
    [invocation setArgument: &myNSApplicationActivationPolicyAccessory atIndex: 2];
    [invocation invoke];

}

我希望有人会觉得这很有用。


4
2018-05-28 14:10



不幸的是,这有效地带回了Dock图标。 - stephencelis
是的,它确实。长号 - david