Change macOS window level with SwiftUI (to make a floating window)

Hi is there a way to change the window level with SwiftUI so I can make a floating window for my mac app? I am using the SwiftUI life cycle

Upvotes: 12

Views: 3575

Answers (3)

josef
josef

Reputation: 1023

Slight update to @mattroberts code for macOS 14:

ContentView()
    .background(WindowAccessor(window: $window))
    .onChange(of: window) { _, newWindow in                    
        if windowFloats {
            newWindow?.level = .floating
        } else {
            newWindow?.level = .normal
        }
     }
     .onChange(of: windowFloats) {                    
         if windowFloats {
             window?.level = .floating
         } else {
             window?.level = .normal
         }
      }

If you just want it to always float you dont need the second onChange(of:)

Upvotes: 0

mattroberts
mattroberts

Reputation: 630

You can use window.level = .floating as Alexander Sandberg put, but you likely will only want to specify this for one window not every one in your app.

That said, take a look at Asperi's post that shows you how to access a window in macOS in SwiftUI: https://stackoverflow.com/a/63439982/10844710

I will summarize the pertinent code here:

#if os(macOS)
import SwiftUI
import AppKit

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?

    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            self.window = view.window
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {}
}
#endif

You then can use it in a window group at the top level of your app like so:

ContentView().background(WindowAccessor(window: $window)).onChange(of: window) { newWindow in
    if windowFloats {
        newWindow?.level = .floating
    } else {
        newWindow?.level = .normal
    }
}.onChange(of: windowFloats) { floats in
    if windowFloats {
        window?.level = .floating
    } else {
        window?.level = .normal
    }
}

You can include your variables as needed:

#if os(macOS)
@State var window: NSWindow?
@AppStorage("windowFloats") private var windowFloats: Bool = false
#endif

Upvotes: 6

Alexander Sandberg
Alexander Sandberg

Reputation: 1883

You can access your windows with NSApplication.shared.windows and set the level for each one.

For instance:

Button("Float windows") {
    for window in NSApplication.shared.windows {
        window.level = .floating
    }
}

Upvotes: 10

Related Questions