How to hide the Dock icon
Asked Answered
D

7

90

I want to make a preference for hiding the Dock icon and showing an NSStatusItem. I can create the StatusItem but I don't know how to remove the icon from Dock. :-/

Any ideas?

Doxia answered 6/3, 2009 at 23:15 Comment(1)
if your app is based on Qt5, you also need to set the envvar QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORMYuu
L
89

I think you are looking for the LSUIElement in the Info.plist

LSUIElement (String). If this key is set to “1”, Launch Services runs the application as an agent application. Agent applications do not appear in the Dock or in the Force Quit window. Although they typically run as background applications, they can come to the foreground to present a user interface if desired.

See a short discussion here about turning it on/off

Likely answered 7/3, 2009 at 0:9 Comment(1)
The link is dead.Olympiaolympiad
S
98

You can use what is called Activation Policy:

Objective-C

// The application is an ordinary app that appears in the Dock and may
// have a user interface.
[NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];

// The application does not appear in the Dock and does not have a menu
// bar, but it may be activated programmatically or by clicking on one
// of its windows.
[NSApp setActivationPolicy: NSApplicationActivationPolicyAccessory];

// The application does not appear in the Dock and may not create
// windows or be activated.
[NSApp setActivationPolicy: NSApplicationActivationPolicyProhibited];

Swift 4

// The application is an ordinary app that appears in the Dock and may
// have a user interface.
NSApp.setActivationPolicy(.regular)

// The application does not appear in the Dock and does not have a menu
// bar, but it may be activated programmatically or by clicking on one
// of its windows.
NSApp.setActivationPolicy(.accessory)

// The application does not appear in the Dock and may not create
// windows or be activated.
NSApp.setActivationPolicy(.prohibited)

This should hide the dock icon.

See also

Stable answered 10/2, 2012 at 0:1 Comment(9)
This is definitely the most elegant solution. Works perfectly.Pryce
+1. NSApplicationActivationPolicyAccessory actually allows the main menu to show up.Kristankriste
From the Apple docs: Currently, NSApplicationActivationPolicyNone and NSApplicationActivationPolicyAccessory may be changed to NSApplicationActivationPolicyRegular, but other modifications are not supported.Tremulous
Single right way. Other solutions are some kinds of hacks. Also it's possible to modify dock behavior on the fly.Hubbell
a great way!simplest way!Elysia
The latest header comment says: "In OS X 10.9, any policy may be set; prior to 10.9, the activation policy may be changed to NSApplicationActivationPolicyProhibited or NSApplicationActivationPolicyRegular, but may not be changed to NSApplicationActivationPolicyAccessory. This returns YES if setting the activation policy is successful, and NO if not."Kemppe
This seems to be the Apple-intended wayEspy
This is showing app icon in doc and removing it from dock immediately. I dont want to make any changes in dock. Any hint.Chalcocite
Amazingly helpfulThomasinathomasine
L
89

I think you are looking for the LSUIElement in the Info.plist

LSUIElement (String). If this key is set to “1”, Launch Services runs the application as an agent application. Agent applications do not appear in the Dock or in the Force Quit window. Although they typically run as background applications, they can come to the foreground to present a user interface if desired.

See a short discussion here about turning it on/off

Likely answered 7/3, 2009 at 0:9 Comment(1)
The link is dead.Olympiaolympiad
B
51

To do it while abiding to the Apple guidelines of not modifying application bundles and to guarantee that Mac App Store apps/(Lion apps ?) will not have their signature broken by info.plist modification you can set LSUIElement to 1 by default then when the application launches do :

ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);

to show it's dock icon, or bypass this if the user chose not to want the icon.

There is but one side effect, the application's menu is not shown until it losses and regains focus.

Source: Making a Checkbox Toggle The Dock Icon On and Off

Personally i prefer not setting any Info.plist option and use TransformProcessType(&psn, kProcessTransformToForegroundApplication) or TransformProcessType(&psn, kProcessTransformToUIElementApplication) based on what is the user setting.

Bullington answered 14/1, 2011 at 0:11 Comment(2)
Great tip! Thanks! You will always want hide the Dock icon this way in order to make sure your signed app works.Godless
Solutions derived from this codepath don't allow for an app that actually wants to be LSUIElement YES (as in, have no menubar, etc). Toggling the process in this fashion will cause a menu to be shown as stated in the answer. I certainly respect that this is the closest thing to an answer for this overlooked functionality, but it isn't a precise solution. I tell users to just manually add the app to the Dock if they want an icon there.Rumilly
D
30

In Xcode it is shown as "Application is agent (UIElement)" and it is Boolean.

In your Info.plist control-click to an empty space and select "Add Row" from the menu Type "Application is agent (UIElement)" Set it YES.

TO make it optional I added the following line to my code (thanks Valexa!)

 // hide/display dock icon
if (![[NSUserDefaults  standardUserDefaults] boolForKey:@"hideDockIcon"]) {
    //hide icon on Dock
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
} 
Demonology answered 24/5, 2011 at 12:28 Comment(0)
M
12

Update for Swift: (Use both ways has been presented above, they have the same result)

public class func toggleDockIcon_Way1(showIcon state: Bool) -> Bool {
    // Get transform state.
    var transformState: ProcessApplicationTransformState
    if state {
        transformState = ProcessApplicationTransformState(kProcessTransformToForegroundApplication)
    }
    else {
        transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication)
    }

    // Show / hide dock icon.
    var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))
    let transformStatus: OSStatus = TransformProcessType(&psn, transformState)
    return transformStatus == 0
}

public class func toggleDockIcon_Way2(showIcon state: Bool) -> Bool {
    var result: Bool
    if state {
        result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.Regular)
    }
    else {
        result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.Accessory)
    }
    return result
}
Medieval answered 9/10, 2014 at 16:58 Comment(1)
This is showing app icon in doc and removing it from dock immediately. I dont want to make any changes in dock. Any hint.Chalcocite
A
4

After trying different variants I still had issues shown below:

  1. App menu not clickable after App icon in Dock is enabled (after setting NSApplication.ActivationPolicy.regular). You need first switch to some other app (e.g. Finder.app) and then switch back to your app to make App menu working as expected.
  2. App windows went backward/hidden after App icon in Dock is disabled (after setting NSApplication.ActivationPolicy.accessory). You need to launch "Mission control" to reveal App windows.

To solve above issues I made an extension:

import AppKit

extension NSApplication {
   public enum Dock {
   }
}

extension NSApplication.Dock {

   public enum MenuBarVisibiityRefreshMenthod: Int {
      case viaMenuVisibilityToggle, viaSystemAppActivation
   }

   public static func refreshMenuBarVisibiity(method: MenuBarVisibiityRefreshMenthod) {
      switch method {
      case .viaMenuVisibilityToggle:
         DispatchQueue.main.async { // Async call not reaaly needed. But intuition tells to leave it.
            // See: cocoa - Hiding the dock icon without hiding the menu bar - Stack Overflow: https://mcmap.net/q/245972/-hiding-the-dock-icon-without-hiding-the-menu-bar
            NSMenu.setMenuBarVisible(false)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // Without delay windows were not always been brought to front.
               NSMenu.setMenuBarVisible(true)
               NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
            }
         }
      case .viaSystemAppActivation:
         DispatchQueue.main.async { // Async call not reaaly needed. But intuition tells to leave it.
            if let dockApp = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first {
               dockApp.activate(options: [])
            } else if let finderApp = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.finder").first {
               finderApp.activate(options: [])
            } else {
               assertionFailure("Neither Dock.app not Finder.app is found in system.")
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // Without delay windows were not always been brought to front.
               NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
            }
         }
      }
   }

   public enum AppIconDockVisibilityUpdateMethod: Int {
      case carbon, appKit
   }

   @discardableResult
   public static func setAppIconVisibleInDock(_ shouldShow: Bool, method: AppIconDockVisibilityUpdateMethod = .appKit) -> Bool {
      switch method {
      case .appKit:
         return toggleDockIconViaAppKit(shouldShow: shouldShow)
      case .carbon:
         return toggleDockIconViaCarbon(shouldShow: shouldShow)
      }
   }

   private static func toggleDockIconViaCarbon(shouldShow state: Bool) -> Bool {
      // Get transform state.
      let transformState: ProcessApplicationTransformState
      if state {
         transformState = ProcessApplicationTransformState(kProcessTransformToForegroundApplication)
      } else {
         transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication)
      }

      // Show / hide dock icon.
      var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))
      let transformStatus: OSStatus = TransformProcessType(&psn, transformState)
      return transformStatus == 0
   }

   private static func toggleDockIconViaAppKit(shouldShow state: Bool) -> Bool {
      let newPolicy: NSApplication.ActivationPolicy = state ? .regular : .accessory
      let result = NSApplication.shared.setActivationPolicy(newPolicy)
      return result
   }
}

Usage:

Precondition: The Info.plist setting LSUIElement is not present or set to value NO.

   private func hideDock() {
      log.debug("Will hide app from dock.")
      let status = NSApplication.Dock.setAppIconVisibleInDock(false)
      log.debug("Status is: \(status)")
      NSApplication.Dock.refreshMenuBarVisibiity(method: .viaMenuVisibilityToggle)
   }

   private func showDock() {
      log.debug("Will show app in dock.")
      let status = NSApplication.Dock.setAppIconVisibleInDock(true)
      log.debug("Status is: \(status)")
      // The method `viaMenuVisibilityToggle` not working. Menu itens non-clickable
      NSApplication.Dock.refreshMenuBarVisibiity(method: .viaSystemAppActivation)
   }
Alastair answered 20/6, 2021 at 15:24 Comment(3)
Hi, i'm trying to use this, I placed the extension in a file and the function in the view controller. But the function errors with "Type 'Self' has no member 'Dock'" and "Cannot infer contextual base in reference to member 'viaMenuVisibilityToggle'". any idea where i'm going wrong?Colchicine
Try to call NSApplication.Dock.setAppIconVisibleInDock(...) instead of Self.Dock.setAppIconVisibleInDock(...).Alastair
@Colchicine Don't forget to upvote if it works :)Alastair
J
3

If you want to make it a user preference then you can't use UIElement. UIElement resides in the application bundle you shouldn't edit any of the files in the app bundle as this will invalidate the bundles signature.

The best solution I've found is based on this excellent article . My solution is based on the comment by Dan. In short, There's no way to do this with Cocoa, but it is possible with a tiny bit of Carbon code.

The article also suggests making a helper app that handles the dock icon exclusively. The main app then starts and kills this app depending on the users preferences. This approach strikes me as being more robust than using the Carbon code, but I haven't tried it yet.

Jueta answered 10/3, 2009 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.