Search code examples
swiftmacoscocoadisplaymultiple-monitors

How can I programmatically display a window on a specific screen (i.e., main display as opposed to external displays) in Swift 5? [MacOS; Xcode 15]


I am facing difficulty presenting my app window on a specific screen (i.e., the main display as opposed to my external display). The reason why I want to display my app on the main display is that I would like to blank out my external display later on by using CGDisplayCapture(CGDirectDisplayID(2)).

I found several suggestions and implementations as follows.

Swift 3/macOS: Open window on certain screen

How to change the NSScreen a NSWindow appears on

Show window on multiple displays on os x

how to move NSWindow to a particular screen?

However, I cannot figure out how to actualize this in Swift 5. This could be because the previous posts got outdated. For example, the following my code (based on Swift 3/macOS: Open window on certain screen) does not work... it does not do anything.

override func viewDidLoad() {
    super.viewDidLoad()
    
    //set up the main display as the display where window shows up
    let screens = NSScreen.screens
    var pos = NSPoint()
    pos.x = screens[0].visibleFrame.midX
    pos.y = screens[0].visibleFrame.midY
    self.view.window?.setFrameOrigin(pos)

I would appreciate it if I could hear how to implement this.

Added:

The answer provided to Cocoa show NSWindow on a specific screen seems to be written in objective-c. Unfortunately, I do not understand objective-c.

Added:

Thanks to the answer provided by @al45tair, I tried to use windowController in appDegate as follows:

 func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application
    let storyboard = NSStoryboard(name: "Main", bundle: nil)
    if let windowController = storyboard.instantiateController(withIdentifier: "mainWindow") as? NSWindowController {
        
        //keep the window top
        windowController.window?.level = .floating


        //set up the main display as the display where window shows up
        let screens = NSScreen.screens
        var pos = NSPoint()
        pos.x = screens[0].visibleFrame.midX
        pos.y = screens[0].visibleFrame.midY
        windowController.window?.setFrameOrigin(pos)


        windowController.window?.zoom(self)
        windowController.window?.level = .floating
        windowController.window?.backgroundColor = NSColor.white

        //stop the user from moving window
        windowController.window?.isMovable = false
        //disable resizable mode
        windowController.window?.styleMask.remove(.resizable)
        windowController.showWindow(self)

    } else {
        print ("failed to show the window")
    }
 }

I also ticked off the main window controller as initial controller (because otherwise, I get two same initial windows). However, I realized by using this method, I cannot hide and show the window by using self.view.window?.setIsVisible(false) (or true) in ViewController.swift. What am I doing wrong?


Solution

  • Thanks to the question asked by @Willeke, I was able to use the following code to display the window on the main screen (as opposed to external displays). I just needed to use DispatchQueue.main.async {}.

       override func viewDidLoad() {
             super.viewDidLoad()
             DispatchQueue.main.async {
                 
                 //set up the main display as the display where window shows up
                 let screens = NSScreen.screens
                 var pos = NSPoint()
                 pos.x = screens[0].visibleFrame.midX
                 pos.y = screens[0].visibleFrame.midY
                 self.view.window?.setFrameOrigin(pos)
                 
             }
        }