如何隐藏 Dock 图标

发布于 2024-07-14 06:14:29 字数 115 浏览 7 评论 0原文

我想选择隐藏 Dock 图标并显示 NSStatusItem。 我可以创建 StatusItem,但我不知道如何从 Dock 中删除该图标。 :-/

有任何想法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

牵你手 2024-07-21 06:14:30

您可以使用所谓的激活策略:

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)

这应该隐藏停靠图标。

另请参阅

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

假面具 2024-07-21 06:14:30

我认为您正在 Info.plist 中寻找 LSUIElement

LSUIElement(字符串)。 如果此键设置为“1”,则启动服务将应用程序作为代理应用程序运行。 代理应用程序不会出现在 Dock 或“强制退出”窗口中。 尽管它们通常作为后台应用程序运行,但如果需要,它们可以来到前台呈现用户界面。

请参阅此处有关打开/关闭它的简短讨论

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

擦肩而过的背影 2024-07-21 06:14:30

要执行此操作,同时遵守不修改应用程序包的 Apple 准则,并保证 Mac App Store 应用程序/(Lion 应用程序?)的签名不会因 info.plist 修改而损坏,您可以默认将 LSUIElement 设置为 1,然后当应用程序启动时执行以下操作:

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

显示其停靠图标,或者如果用户选择不想要该图标则绕过此图标。

只有一个副作用,应用程序的菜单只有在失去焦点并重新获得焦点时才会显示。

来源:制作复选框以打开和关闭 Dock 图标

就我个人而言,我更喜欢不设置任何 Info.plist 选项,并根据具体情况使用 TransformProcessType(&psn, kProcessTransformToForegroundApplication)TransformProcessType(&psn, kProcessTransformToUIElementApplication)是用户设置。

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.

可爱咩 2024-07-21 06:14:30

在 Xcode 中,它显示为“应用程序是代理(UIElement)”并且它是布尔值。

在 Info.plist 控件中单击空白区域,然后从菜单中选择“添加行”
类型“应用程序是代理(UIElement)”
将其设置为“是”。

为了使其可选,我将以下行添加到我的代码中(感谢 Valexa!)

 // hide/display dock icon
if (![[NSUserDefaults  standardUserDefaults] boolForKey:@"hideDockIcon"]) {
    //hide icon on Dock
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
} 

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);
} 
软的没边 2024-07-21 06:14:30

Swift 更新:(使用上面介绍的两种方法,它们具有相同的结果)

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
}

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
}
所有深爱都是秘密 2024-07-21 06:14:30

尝试不同的变体后,我仍然遇到如下所示的问题:

  1. Dock 中的应用程序图标启用后,应用程序菜单不可点击(设置NSApplication.ActivationPolicy.regular)。 您需要首先切换到其他应用程序(例如Finder.app),然后切换回您的应用程序以使应用程序菜单按预期工作。
  2. Dock 中的应用程序图标禁用后(设置 NSApplication.ActivationPolicy.accessory 后),应用程序窗口向后/隐藏。 您需要启动“任务控制”才能显示应用程序窗口。

为了解决上述问题,我做了一个扩展:

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://stackoverflow.com/questions/23313571/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
   }
}

用法:

前提条件:Info.plist设置LSUIElement不存在或设置为值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)
   }

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://stackoverflow.com/questions/23313571/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)
   }
檐上三寸雪 2024-07-21 06:14:30

如果你想让它成为用户首选项,那么你不能使用 UIElement。 UIElement 驻留在应用程序包中,您不应编辑应用程序包中的任何文件,因为这将使包签名无效。

我找到的最佳解决方案基于 这篇优秀的文章。 我的解决方案基于 Dan 的评论。 简而言之,用 Cocoa 无法做到这一点,但用一点点 Carbon 代码就可以做到。

该文章还建议制作一个专门处理停靠栏图标的帮助应用程序。 然后主应用程序根据用户的偏好启动并终止该应用程序。 这种方法让我觉得比使用 Carbon 代码更健壮,但我还没有尝试过。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文