Opening NSWindow on a connected display

I came across this question on S.O. while searching for how to open a new NSWindow / NSView on a connected display.

I converted the obj-c code to Swift as well as I could, but I cannot get it to work. I have the method set up inside an IBAction and I linked the action to a button I placed in the ViewController's View titled "Show External Display."

What am I doing wrong? Is there a good resource that can help me for working with connected displays using Cocoa/AppKit? I've read the docs, but still can't seem to get this working.

import Cocoa

class ViewController: NSViewController {

    var externalDisplay: NSScreen?
    var fullScreenWindow: NSWindow?
    var fullScreenView: NSView?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    @IBAction func showExternalDisplayButtonClicked(sender: Any?) {
        // Open a window on the external display if present
        if NSScreen.screens.count > 1 {
            externalDisplay = NSScreen.screens.last
            let externalDisplayRect = externalDisplay!.frame
            fullScreenWindow = NSWindow(contentRect: externalDisplayRect, styleMask: .borderless, backing: .buffered, defer: true, screen: externalDisplay)
            fullScreenWindow!.level = .normal
            fullScreenWindow!.isOpaque = false
            fullScreenWindow!.hidesOnDeactivate = false
            fullScreenWindow!.backgroundColor = .red
            let viewRect = NSRect(x: 0, y: 0, width: externalDisplay!.frame.width, height: externalDisplay!.frame.height)
            fullScreenView = NSView(frame: viewRect)
            fullScreenWindow!.contentView = fullScreenView
            fullScreenWindow!.makeKeyAndOrderFront(self)
        }
    }

}
  • Can you explain what is the problem of your current code? Does it not compile? Or crashes on runtime? Or the result is unexpected?

  • The window was not displaying on the external display, but yet I was not getting any kind of error. Someone else helped me on another site. I will post the answer.

Add a Comment

Accepted Reply

As pointed out on another site, the problem was caused by me setting the styleMask as .borderless without .titled, which prevents the window from becoming key or main according to this.

Here's my updated code that works in Swift 5.5:

import Cocoa

class ViewController: NSViewController {

    var externalDisplay: NSScreen?
    var fullScreenWindow: NSWindow?
    var fullScreenView: NSView?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    @IBAction func showExternalDisplayButtonClicked(sender: Any?) {
        // Open the external display window if one is present
        if NSScreen.screens.count > 1 {
            externalDisplay = NSScreen.screens.last
            let mask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable, .borderless]
            fullScreenWindow = NSWindow(contentRect: externalDisplay!.frame, styleMask: mask, backing: .buffered, defer: true, screen: externalDisplay)
            fullScreenWindow!.level = .normal
            fullScreenWindow!.isOpaque = false
            fullScreenWindow!.hidesOnDeactivate = false
            fullScreenWindow!.backgroundColor = .red
            let viewRect = NSRect(x: 0, y: 0, width: externalDisplay!.frame.width, height: externalDisplay!.frame.height)
            fullScreenView = NSView(frame: viewRect)
            fullScreenWindow!.contentView = fullScreenView
            fullScreenView?.window?.toggleFullScreen(self)
        }

    }

}

Replies

As pointed out on another site, the problem was caused by me setting the styleMask as .borderless without .titled, which prevents the window from becoming key or main according to this.

Here's my updated code that works in Swift 5.5:

import Cocoa

class ViewController: NSViewController {

    var externalDisplay: NSScreen?
    var fullScreenWindow: NSWindow?
    var fullScreenView: NSView?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    @IBAction func showExternalDisplayButtonClicked(sender: Any?) {
        // Open the external display window if one is present
        if NSScreen.screens.count > 1 {
            externalDisplay = NSScreen.screens.last
            let mask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable, .borderless]
            fullScreenWindow = NSWindow(contentRect: externalDisplay!.frame, styleMask: mask, backing: .buffered, defer: true, screen: externalDisplay)
            fullScreenWindow!.level = .normal
            fullScreenWindow!.isOpaque = false
            fullScreenWindow!.hidesOnDeactivate = false
            fullScreenWindow!.backgroundColor = .red
            let viewRect = NSRect(x: 0, y: 0, width: externalDisplay!.frame.width, height: externalDisplay!.frame.height)
            fullScreenView = NSView(frame: viewRect)
            fullScreenWindow!.contentView = fullScreenView
            fullScreenView?.window?.toggleFullScreen(self)
        }

    }

}