Home > OS >  Application main NSMenu not responding without window reactivation
Application main NSMenu not responding without window reactivation

Time:08-28

I'm trying a minimalistic Cocoa app as described in this page using code:

#import <Cocoa/Cocoa.h>;
int main ()
{
    [NSAutoreleasePool new];
    [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    id menubar = [[NSMenu new] autorelease];
    id appMenuItem = [[NSMenuItem new] autorelease];
    [menubar addItem:appMenuItem];
    [NSApp setMainMenu:menubar];
    id appMenu = [[NSMenu new] autorelease];
    id appName = [[NSProcessInfo processInfo] processName];
    id quitTitle = [@"Quit " stringByAppendingString:appName];
    id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
        action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
    [appMenu addItem:quitMenuItem];
    [appMenuItem setSubmenu:appMenu];
    id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
        styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
            autorelease];
    [window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    [window setTitle:appName];
    [window makeKeyAndOrderFront:nil];
    [NSApp activateIgnoringOtherApps:YES];
    [NSApp run];
    return 0;
}

My issue is, that the application menu is not responding until I reactivate its window (activating another app and then clicking back to my app window).

Any idea why and how to resolve it?

CodePudding user response:

Ok... I found the solution... I must replace

    [NSApp activateIgnoringOtherApps:YES];

with:

    dispatch_async(dispatch_get_main_queue(), ^{
        [NSApp activateIgnoringOtherApps:YES];
    });

I don't know why, but it works.

CodePudding user response:

The AppKit is still in your app's startup phase when you're attempting to call [NSApp activateIgnoringOtherApps:YES];. The correct way to do it is to handle it through NSApplicationDelegate:

@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (strong, nonatomic, class, readonly) AppDelegate* sharedInstance;
@end
@implementation AppDelegate
  (instancetype)sharedInstance {
    static AppDelegate *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [AppDelegate new];
    });
    return sharedInstance;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [NSApp activateIgnoringOtherApps:YES];
}
@end

int main ()
{
    [NSAutoreleasePool new];
    [NSApplication sharedApplication];
    NSApplication.sharedApplication.delegate = [AppDelegate sharedInstance];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    id menubar = [[NSMenu new] autorelease];
    id appMenuItem = [[NSMenuItem new] autorelease];
    [menubar addItem:appMenuItem];
    [NSApp setMainMenu:menubar];
    id appMenu = [[NSMenu new] autorelease];
    id appName = [[NSProcessInfo processInfo] processName];
    id quitTitle = [@"Quit " stringByAppendingString:appName];
    id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
        action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
    [appMenu addItem:quitMenuItem];
    [appMenuItem setSubmenu:appMenu];
    id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
        styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
            autorelease];
    [window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    [window setTitle:appName];
    [window makeKeyAndOrderFront:nil];
    [NSApp run];
    return 0;
} 

As a bonus the delegate's - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { will get invoked before your:

dispatch_async(dispatch_get_main_queue(), ^{
    [NSApp activateIgnoringOtherApps:YES];
});

hinting it's the correct way to do it.

  • Related