Я хочу настроить скрытие значка Dock и отображение NSStatusItem
. Я могу создать StatusItem, но не знаю, как удалить значок из Dock. : - /
Любые идеи?
Я хочу настроить скрытие значка Dock и отображение NSStatusItem
. Я могу создать StatusItem, но не знаю, как удалить значок из Dock. : - /
Любые идеи?
Я думаю, вы ищете LSUIElement
в Info.plist
LSUIElement (Строка). Если для этого ключа установлено значение «1», Launch Services запускает приложение как приложение-агент. Приложения агента не отображаются в доке или в окне принудительного выхода. Хотя они обычно работают как фоновые приложения, при желании они могут выходить на передний план, чтобы представить пользовательский интерфейс.
См. Краткое обсуждение здесь о его включении и выключении.
Вы можете использовать то, что называется Политикой активации:
// 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)
Это должно скрыть значок док-станции.
NSRunningApplicationActivationPolicy
.NSApplicationActivationPolicyAccessory
фактически позволяет отображать главное меню.
- person Mark Bao; 23.06.2013
Чтобы сделать это при соблюдении руководящих принципов Apple о том, чтобы не изменять пакеты приложений и гарантировать, что приложения Mac App Store / (приложения Lion?) Не будут иметь свою подпись, нарушенную модификацией info.plist, вы можете установить LSUIElement на 1 по умолчанию, тогда, когда запуски приложений делают:
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
, чтобы отобразить значок док-станции, или обойти это, если пользователь решил, что значок не нужен.
Есть только один побочный эффект: меню приложения не отображается, пока оно не потеряет и не вернет фокус.
Источник: Установка флажка для включения и выключения значка док-станции
Лично я предпочитаю не устанавливать никаких опций Info.plist и использовать TransformProcessType(&psn, kProcessTransformToForegroundApplication)
или TransformProcessType(&psn, kProcessTransformToUIElementApplication)
в зависимости от настроек пользователя.
В 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);
}
Обновление для 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
}
Если вы хотите сделать это предпочтением пользователя, вы не можете использовать UIElement. UIElement находится в пакете приложения, вам не следует редактировать какие-либо файлы в пакете приложения, так как это сделает подпись пакета недействительной.
Лучшее решение, которое я нашел, основано на эту отличную статью. Мое решение основано на комментарии Дэна. Короче говоря, это невозможно сделать с помощью Какао, но это возможно с помощью крошечного фрагмента кода углерода.
В статье также предлагается создать вспомогательное приложение, которое обрабатывает только значок док-станции. Затем запускается основное приложение и завершает работу этого приложения в зависимости от предпочтений пользователя. Этот подход кажется мне более надежным, чем использование кода Carbon, но я еще не пробовал его.
После того, как я попробовал разные варианты, у меня все еще были проблемы, показанные ниже:
NSApplication.ActivationPolicy.regular
). Вам нужно сначала переключиться на другое приложение (например, Finder.app), а затем снова переключиться на свое приложение, чтобы меню приложения работало должным образом.NSApplication.ActivationPolicy.accessory
). Вам нужно запустить Mission Control, чтобы открыть окна приложений.Чтобы решить вышеуказанные проблемы, я сделал расширение:
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)
}
QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM
- person Chris Dolan   schedule 01.05.2015