83

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

有任何想法吗?

4

7 回答 7

88

我想你正在寻找LSUIElementInfo.plist

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

在此处查看有关打开/关闭它的简短讨论

于 2009-03-07T00:09:22.843 回答
86

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

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];

斯威夫特 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)

这应该隐藏停靠图标。

也可以看看

于 2012-02-10T00:01:56.437 回答
50

要做到这一点,同时遵守 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)基于用户设置。

于 2011-01-14T00:11:14.950 回答
29

在 Xcode 中显示为“Application is agent (UIElement)”,它是布尔值。

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

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

 // hide/display dock icon
if (![[NSUserDefaults  standardUserDefaults] boolForKey:@"hideDockIcon"]) {
    //hide icon on Dock
    ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
} 
于 2011-05-24T12:28:57.053 回答
12

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
}
于 2014-10-09T16:58:05.240 回答
3

如果你想让它成为用户偏好,那么你不能使用 UIElement。UIElement 位于应用程序包中,您不应编辑应用程序包中的任何文件,因为这将使包签名无效。

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

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

于 2009-03-10T13:05:51.157 回答
1

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

  1. 启用Dock 中的应用程序图标后(设置后),应用程序菜单不可点击。您需要先切换到其他应用程序(例如 Finder.app),然后再切换回您的应用程序以使应用程序菜单按预期工作。NSApplication.ActivationPolicy.regular
  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不存在或设置为 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)
   }
于 2021-06-20T15:24:52.410 回答