I maintain a custom clock app (just for me, not the app store) that I use to view the time on my computer. I want it to stay over all other windows, including apps (and videos) in fullscreen. When I updated to MacOS Sequoia (15.0) my old solution stopped working. I can still get the app to move to new spaces as I switch between them, but I cannot get a window to remain on the screen while transitioning another app to full screen. This is the minimum code I used to recreate the problem in a fresh SwiftUI app:
@main
struct testtickerApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
let window = NSApplication.shared.windows.first!
window.level = .floating
window.styleMask = .borderless
window.collectionBehavior = [.canJoinAllApplications, .canJoinAllSpaces, .auxiliary, .fullScreenAuxiliary, .ignoresCycle]
}
}
and this is the code that used to work for me, prior to MacOS 15.0 (used in the same applicationDidFinishLaunching
function):
func setupWindow(_ window: NSWindow) {
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.standardWindowButton(NSWindow.ButtonType.closeButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.miniaturizeButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.zoomButton)?.isHidden = true
window.isOpaque = false
window.hasShadow = false
window.level = .floating
window.backgroundColor = NSColor.clear
window.isReleasedWhenClosed = false
window.isMovableByWindowBackground = true
window.collectionBehavior = .canJoinAllSpaces
window.titlebarSeparatorStyle = .none
window.ignoresMouseEvents = true
window.delegate = self
}
based on these answers:
Enable fullscreen for floating window in macOS app
Why does my NSWindow not float above full screen apps when created programmatically? (Swift 5)
How to create whole screen overlay in MacOS with Swift?
Window visible on all spaces (including other fullscreen apps)
How to keep my overlay on top while another app is in Full Screen mode?
and the description of fullScreenAuxiliary
, I would expect my previous solution to still work. Is this a bug? Is there any way to get this working? I have tried closing and redrawing the window, moving it to front, changing LSUIElement
to TRUE
in my info.plist
, and many other things, with essentially no success. I don’t need the solution to be especially pretty (or within Apple’s guidelines), but I would like it to work.
For reasons I don’t understand, this issue seems to be linked to whether the window is created by me or the SwiftUI lifecycle (this is seeming more and more like a bug). The following code works exactly like my old solution used to work for me:
@main
struct testtickerApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
Settings { }
}
}
var myWindow = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 100, height: 100), styleMask: [], backing: .buffered, defer: false)
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.accessory)
myWindow.contentView = NSHostingView(rootView: ContentView())
myWindow.level = .floating
myWindow.collectionBehavior = .canJoinAllSpaces
myWindow.setIsVisible(true)
}
}
Note that I have used the Settings { }
Scene simply to avoid SwiftUI creating a window (based on this answer). The settings trick is not necessary, it just avoids the creation of a window that I don’t want. Also note that setting the content view of myWindow
to be equal to the SwiftUI-made window’s content view does not seem to work; I have to use this NSHostingView
. You can set the window’s collection behavior to be more than just .canJoinAllSpaces
; that just seems to be the crucial one for this particular feature working. The window level also does not seem to be pertinent, it’s just helpful for keeping the window over other windows when not in full screen. I hope this is helpful for others!
As a worse alternate solution, instead of setting the collection behavior, you can set myWindow.isReleasedWhenClosed = false
, and then call myWindow.close()
followed by myWindow.setIsVisible(true)
after the screen is switched to the desired space (I used applicationDidChangeOcclusionState
to check for this). This is much more fiddly but I can imagine use cases where this is preferred.