平時,你不會遇到這個問題,直到你需要寫一個後台程序……
我們都知道 macOS 有一個全局的 Dock,這個東西上會顯示所有正在運行的程序,但如果你的程序是一個菜單欄小程序,或者是一個……輸入法,那你肯定不想讓這個 app 的圖標顯示的 Dock 上,因為這類 app 是要持續在後台運行的。
這時候我們就可以在 Info.plist 中寫 LSBackgroundOnly 字段,這樣這個 app 就會在後台運行,沒有 Dock 圖標,也不會在 cmd+tab 的切換選項中顯示出來了。落格輸入法就是這麼做的,這也是蘋果官方示例中的配置——但這麼做長久以來伴隨著一個問題,那就是如果你這個 app 還是需要顯示必要的設置界面的話,那這個界面會永遠在所有窗口的最下方,也就是說,窗口一彈出就立即被擋住了。甚至,除了標準窗口,其他任何警告彈窗,都是無法顯示的,除非先有一個標準窗口出現,且用戶點擊了一下該窗口的任何位置……
上文這些前提導致了很多問題,我不得不內嵌另外一個 app 來作為落格輸入法的專門設置軟件,然後兩者之間使用 xpc 通信,真的是令人絕望。
直到今天 TIL,我發現原來可以使用 NS應用程序.共享.設置激活策略(.配飾) 在程序中動態改變自己的運行級別,可以動態修改自己為普通 常規的 模式,或者是後台 禁止 模式。顯然,通過看代碼名稱也明白了,原來這個後台模式,是故意不允許彈出窗口的。
1 2 |
/* The application does not appear in the Dock and may not create windows or be activated. This corresponds to LSBackgroundOnly=1 in the Info.plist. This is also the default for unbundled executables that do not have Info.plists. */ case prohibited = 2 |
顯然,如果你聲明程序是後台程序,那它的一切窗口都是不能獲得焦點的,這個問題我一直以來以為是 macOS 的Bug😅
那麼,有沒有別的方案呢?還真的有,除了上文的兩種模式外,macOS 其實還提供了介於兩者中間的模式,既像後台程序那樣不顯示圖標,又能在必要的時候獲取焦點和類似普通程序那樣彈窗:
1 2 |
/* 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. This corresponds to LSUIElement=1 in the Info.plist. */ case accessory = 1 |
在必要的時候動態切換程序的運行模式為 配飾 即可。
當然,如果用代碼太麻煩,我們也可以直接改 Info.plist,把 LSBackgroundOnly ,改為 LSUIE元素 ,即可。
本文由 落格博客 原創撰寫:落格博客 » macOS application 的三種運行模式
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/3531.html
博主您好 我現在遇到了這個問題:正常的邏輯是,我點擊主界面窗口的關閉按鈕,然後DOCK消失,但是彈出一個新窗口,提示進入後台運行。使用了NSApplication.sharedApplication().設置激活策略(0x1),但是這串代碼執行之後,我所有的窗口都不見了,請問如何解決。
我使用的是JAVA的SWT框架。
我沒用過 SWT 框架,不過你試試用alert代替窗口,或者設置窗口為固定最前,就那個popup級別。理論上不應該出現這個情況。
另外你檢查一下傳入 0x1 是否正確,這個我不確定 SWT 如何轉換傳值的,有可能轉成了無法識別的值於是出現意外行為。
目前在M1處理器 13.1上不會出現問題,在intel 10.15.7版本上,我先執行setActivationPolicy(0x1),然後彈窗(就是一個窗口),出現後立馬消失。然後我循環判斷setActivationPolicy(0x1)的返回值,當返回值為true的時候,彈窗將正常。是不是因為setActivationPolicy(0x1)底層是多線程,導致他在我彈窗生成後才執行。(但是即使是這樣,應該也是將彈窗置於最下面,而不是直接一閃而過)
哦哦,那確實,這類API都不是實時生效的,你試試等待個一秒,應該就沒問題了。我確實遇到過類似的問題。
請問有沒有更好的解決方案,因為用戶的設備可能很差,也可能很好,我無法確定等待的時間
有一個思路就監測app運行狀態,比如進入後台之類的?或者看看獲取這個狀態的API的結果,生效後再彈窗就是了。雖然我搞清楚了這個情況,後來由於其他原因,我並沒有用到,我的輸入法設置窗口還是使用了單獨的app實現的。
感謝您的解答,您的這個思路確實有效。但是我有一個疑問,監測APP狀態的時候,有沒有可能,app已經關閉了,但是setActivationPolicy還沒有執行完畢(目前沒有遇到)。那麼我判斷app狀態是關閉的時候,打開了彈窗,隨後setActivationPolicy執行,那麼我的彈窗將再次被其關閉。
所以我新開了一個線程,判斷setActivationPolicy的返回值是不是true,是的話,說明他執行結束了,我就打開彈窗。這樣我的彈窗將一直在setActivationPolicy後執行,不會被關閉。
還有一個疑問就是,我不知道為什麼在我的程序中,執行setActivationPolicy(1)後,會關閉我所有的窗口,他的效果不應該是關閉DOCK,並且將所有窗口置於最下面嗎?
是隱藏……他只是不顯示了,並不是給你關閉了。
另外設置為1是既隱藏又允許顯示,所以窗口的開關只能你自己控制。
你這個情況🤔我也不清楚,抱歉了哈。
請問 :LSBackgroundOnly 在 Info.plist 中如何輸寫
直接在根層級添加一個字段,key就是 LSBackgroundOnly,類型為bool,值為1或者yes