NSPanel (borderless, .nonactivatingPanel, level .floating) disappears the moment another app becomes active — window vanishes from screen and CGWindowList, no error
Context 環境
macOS (Darwin 25 / Sequoia系) / Swift + AppKit / SPM製 LSUIElement(accessory)常駐メニューバーアプリ / NSPanel styleMask [.borderless, .nonactivatingPanel], level = .floating, collectionBehavior [.canJoinAllSpaces, .fullScreenAuxiliary]
What happened 何が起きたか
スクショを常時最前面に浮かせる「画面にピン留め」パネルを NSPanel で実装。orderFrontRegardless() 直後は表示され CGWindowList にも layer=3 で載るのに、ユーザーが他アプリをクリックした瞬間(=自アプリが deactivate した瞬間)にパネルが消えた。エラーもログも一切出ないため、level や collectionBehavior を疑って遠回りした。accessory アプリはほぼ常に非アクティブなので「作った直後だけ見えて、使う場面では見えない」という症状になる。
Root cause 原因
NSPanel は NSWindow と違い hidesOnDeactivate のデフォルトが true。アプリが非アクティブになると AppKit がパネルを自動的に orderOut する仕様で、フローティング常駐用途ではこの既定値が罠になる。level .floating や canJoinAllSpaces では防げない。
Fix 解決策
パネル初期化時に明示的に無効化する: let panel = NSPanel(contentRect: rect, styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: false) panel.hidesOnDeactivate = false // ← これが必須。NSPanel の既定は true panel.isFloatingPanel = true panel.level = .floating panel.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] 検証は CGWindowListCopyWindowInfo で自プロセスのウィンドウが他アプリをアクティブにした後も layer=3 で残ることを確認(macOS 26 相当環境で解決確認済み)。