40

In Objective-C for Cocoa Apps it's possible to use such way to keep window always on top?

How to achieve the same with Swift?

self.view.window?.level = NSFloatingWindowLevel

Causes build error Use of unresolved identifier 'NSFloatingWindowLevel'

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
AlexKorovyansky
  • 4,873
  • 5
  • 37
  • 48

4 Answers4

95

To change the window level you can't do it inside viewDidload because view's window property will always be nil there but it can be done overriding viewDidAppear method or any other method that runs after view is installed in a window (do not use it inside viewDidLoad):

Swift 4 or later

override func viewDidAppear() {
    super.viewDidAppear()
    view.window?.level = .floating
}

For older Swift syntax check the edit history

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 2
    Where exactly that line should go? – Miguel Febres Jun 25 '15 at 02:06
  • 1
    @LeoDabus Why do I get the error: 'Int' is not convertible to 'CGWindowLevelKey'? – Sam Jun 28 '15 at 09:56
  • 3
    Breaks in swift 2.0. Any way arounds? – CalZone Aug 21 '15 at 05:31
  • When set it to .FloatingWindowLevelKey or .MaximumWindowLevelKey, the window stays on top of full screen video, e.g. full screen YouTube video on Chrome, or VLC in full screen mode. This is not exactly what I want. Do you know the correct key which makes it float on top of other windows but below full screen videos? – Şafak Gezer Oct 19 '15 at 09:03
  • @LeoDabus Should be above all apps and windows, not just mine. I've been trying different values of the CGWindowLevelKey enum but I'm starting to think there is no one liner to fix this unfortunately, as I just realized same thing also happens with the iTunes MiniPlayer's always on top option. It's probably because Chrome and VLC claim full screen in an unorthodox way, rather than the regular "full screen as a separate desktop" behavior of maximized Finder windows, or Safari's full screen mode for videos. (both of which do not expose this glitch) I'm open to any suggestions though. – Şafak Gezer Oct 19 '15 at 09:35
  • @LeoDabus I did, along with many other values of the same enum to no avail. It looks like those applications in full screen are still somehow considered regular windows by the system. Will write here if I find a solution. Thank you for your help. – Şafak Gezer Oct 19 '15 at 15:25
  • 1
    @sam try `Int(CGWindowLevelForKey(Int32(kCGMaximumWindowLevelKey)))` – Shakeeb Ahmad Nov 13 '15 at 02:34
  • @LeoDabus This gives me 'CGWindowLevelKey.Type' does not have a member named 'MaximumWindowLevelKey' – Shakeeb Ahmad Nov 13 '15 at 02:42
  • could you please help me, I need set button on the top of view stack. There is my case. I work with camera and camera dynamically set outputs in custom view , and such way override my static button... I need put my button on the top of camera preview to make it visible to user. Did you get my idea? – Sirop4ik Feb 02 '17 at 11:17
4

I would prefer this way. This ignores all other active apps, and makes your app upfront.

    override func viewWillAppear() {            
        NSApplication.sharedApplication().activateIgnoringOtherApps(true)
    }
3

While the other answers are technically correct - when your app will or did resigns active, setting the window level to .floating will have no effect.

.floating means on top of all the windows from the app you are working on, it means not on top of all apps windows.

Yes there are other levels available you could set, like kCGDesktopWindowLevel which you can and should not set in swift to make your window float above all.

None of them will change the fact that your window will go behind the focused and active apps window. To circumvent i chose to observe if the app resigns active notification and act accordingly.

var observer : Any;

override func viewDidLoad() {
    super.viewDidLoad()

    observer = NotificationCenter.default.addObserver(
        forName: NSApplication.didResignActiveNotification, 
        object: nil, 
        queue: OperationQueue.main ) { (note) in

        self.view.window?.level = .floating;

        // you can also make your users hate you, to take care, don't use them.
        //NSApplication.shared.activate(ignoringOtherApps: true)
        //self.view.window?.orderFrontRegardless();
    }
}

another way could be subclassing NSWindow and override the property .level with an always returning .floating, but the code above is less work and keeps control in the place where you want to set the window floating.

Ol Sen
  • 3,163
  • 2
  • 21
  • 30
3

I spent a long time trying to make this work. I then realized there was a simple answer, just worded in a different way. Here it is: Change macOS window level with SwiftUI (to make a floating window)

As explained there:

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

    for window in NSApplication.shared.windows {
        window.level = .floating
    }

EDIT: you can use other levels, including .screenSaver (highest, I think) and ```.normal`` if you want to return to standard behavior. Source: https://developer.apple.com/documentation/appkit/nswindow/level

JPC
  • 31
  • 4