我想隐藏Dock图标并显示NSStatusItem
。
我可以创建StatusItem但我不知道如何从Dock中删除图标。 : - /
有什么想法吗?
答案 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)
您可以使用所谓的激活策略:
// 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];
// 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)
尝试不同的变体后,我仍然遇到如下问题:
NSApplication.ActivationPolicy.regular
后)。您需要先切换到其他一些应用(例如 Finder.app),然后再切换回您的应用,以使应用菜单按预期工作。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)
}