如何隐藏Dock图标

时间:2009-03-06 23:15:39

标签: macos cocoa icons dock lsuielement

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

有什么想法吗?

7 个答案:

答案 0 :(得分:73)

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

  

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

请参阅有关打开/关闭

的简短讨论here

答案 1 :(得分:48)

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

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

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

只有一个副作用,应用程序的菜单在丢失并重新获得焦点之前不会显示。

来源:Making a Checkbox Toggle The Dock Icon On and Off

我个人不希望设置任何Info.plist选项,并根据用户设置使用TransformProcessType(&psn, kProcessTransformToForegroundApplication)TransformProcessType(&psn, kProcessTransformToUIElementApplication)

答案 2 :(得分:47)

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

目标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)

这应隐藏停靠栏图标。

另见

答案 3 :(得分:26)

在Xcode 4中,它显示为“Application is agent(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);
} 

答案 4 :(得分:9)

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
}

答案 5 :(得分:2)

如果您想使其成为用户首选项,则无法使用UIElement。 UIElement位于应用程序包中,您不应编辑应用程序包中的任何文件,因为这将使包签名无效。

我发现的最佳解决方案基于this excellent article。我的解决方案基于Dan的评论。简而言之,Cocoa没有办法做到这一点,但只需要一点碳代码即可。

该文章还建议制作一个专门处理停靠图标的帮助应用程序。然后主应用程序启动并根据用户首选项杀死此应用程序。这种方法让我觉得比使用Carbon代码更强大,但我还没有尝试过。

答案 6 :(得分:0)

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

  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 = Self.Dock.setAppIconVisibleInDock(false)
      log.debug("Status is: \(status)")
      Self.Dock.refreshMenuBarVisibiity(method: .viaMenuVisibilityToggle)
   }

   private func showDock() {
      log.debug("Will show app in dock.")
      let status = Self.Dock.setAppIconVisibleInDock(true)
      log.debug("Status is: \(status)")
      // The method `viaMenuVisibilityToggle` not working. Menu itens non-clickable
      Self.Dock.refreshMenuBarVisibiity(method: .viaSystemAppActivation)
   }