Question

I use the ideas in this thread to hide the dock icon of my app optionally. If the dock icon is shown after all, the menu bar should be shown too. Only with Jiulong's answer I haven't been able to make this work. The menu bar is still hidden.

So basically 'Application is agent' is set to '1' in the InfoPList, and this code is used :

if (![[NSUserDefaults standardUserDefaults] boolForKey:@"LaunchAsAgentApp"]) {   
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
    SetSystemUIMode(kUIModeNormal, 0);
    [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.apple.dock"    options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifier:nil];
    [[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
}

So why doesn't the menu bar show up, until I hide and refocus the app? Is there any fix for this? I saw that the 'Quick Search Box' for mac app doesn't show the menu bar upon launching either...


EDIT : I contacted Apple, and they gave me a carbon and a non-carbon solution. Given a new project with 'Application is Agent' set to 'YES' in the Plist file, then this code could be used in the AppDelegate class :

#define USE_CARBON  0

//
//  Note: NSLogDebug is defined in the projects pre-compiled (.pch) file
//

@implementation AppDelegate
{
    BOOL show_icon;
}

// Application will finish launching
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
    NSLogDebug();
    NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
    if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]]) {
        // exit(173);
    }

#if 1
    show_icon = YES;
#else
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSString *hasDockIconDefaultsKey = @"Has Dock Icon?";
    // note: toggles value on each run (normally set from user pref pannel)
    show_icon = [userDefaults boolForKey:hasDockIconDefaultsKey];
    [userDefaults setBool:!show_icon forKey:hasDockIconDefaultsKey];
#endif // if 1
    if (show_icon) {
        [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
        [NSApp setPresentationOptions:NSApplicationPresentationDefault];
        [NSMenu setMenuBarVisible:NO];
        [NSMenu setMenuBarVisible:YES];
    }

    [NSApp activateIgnoringOtherApps:YES];
}   // applicationWillFinishLaunching

// Application did finish launching
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSLogDebug();
    // Insert code here to initialize your application
    if (show_icon) {
#if USE_CARBON
        ProcessSerialNumber psn = {0, kCurrentProcess};
        OSStatus returnCode = TransformProcessType(&psn, kProcessTransformToForegroundApplication);
        if (noErr != returnCode) {
            NSLog(@"TransformProcessType error: %d (0x%0X)", returnCode, returnCode);
        }

        ProcessSerialNumber psnx = {0, kNoProcess};
        GetNextProcess(&psnx);
        SetFrontProcess(&psnx);
#else   // if 0
        NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
        NSRunningApplication * menuBarOwningApplication = [sharedWorkspace menuBarOwningApplication];
        (void) [menuBarOwningApplication activateWithOptions:NSApplicationActivateIgnoringOtherApps];
#endif
        [self performSelector:@selector(setFront) withObject:nil afterDelay:0.];
    }
}   // applicationDidFinishLaunching

// Close app when main window is closed
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
    return (YES);
}

- (void)setFront;
{
#if USE_CARBON
    ProcessSerialNumber psn = {0, kCurrentProcess};
    SetFrontProcess(&psn);
#else // if USE_CARBON
    [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
#endif  // USE_CARBON
}

@end

Note that I filed a bug report too.

Here's a Swift version of the non-carbon solution :

func applicationWillFinishLaunching(_ notification: Notification) {
    if  showIcon {
        NSApp.setActivationPolicy(.regular)
        NSApp.presentationOptions = []
        NSMenu.setMenuBarVisible(false)
        NSMenu.setMenuBarVisible(true)
    }
    NSApp.activate(ignoringOtherApps: true)
}

func applicationDidFinishLaunching(_ aNotification: Notification) {
    NSApplication.shared.activate(ignoringOtherApps: true)
    if  showIcon {
        let workspace = NSWorkspace.shared
        let application = workspace.menuBarOwningApplication
        application?.activate(options: .activateIgnoringOtherApps)
        self.perform(#selector(activate), with: nil, afterDelay: 0.0)
    }
}

@objc private func activate() {  
    NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
}
Was it helpful?

Solution

First, you should use -[NSApplication setActivationPolicy:] rather than TransformProcessType() and -[NSApplication setPresentationOptions:] rather than SetSystemUIMode() in modern code. If switching to those is not enough to fix the problem with the menu bar not updating, I recommend that you try using -setPresentationOptions: or +[NSMenu setMenuBarVisible:] to hide the menu bar and then immediately reverse that operation.

Also, drop that business with trying to activate the Dock.

So, something like:

if (![[NSUserDefaults standardUserDefaults] boolForKey:@"LaunchAsAgentApp"]) {
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    [NSApp setPresentationOptions:NSApplicationPresentationDefault]; // probably not necessary since it's the default
    [NSMenu setMenuBarVisible:NO]; // these two lines may not be necessary, either; using -setActivationPolicy: instead of TransformProcessType() may be enough
    [NSMenu setMenuBarVisible:YES];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top