Swift 3/Macos: Open Window on Certain Screen

Swift 3/macOS: Open window on certain screen

Each screen from NSScreen.screens() has a visibleFrame property that tells you the global frame rectangle.

You can then set your window origin to fit inside the frame rect coordinates of whatever screen you want. Objective-C answers can be seen here and here.

This does mean you have to write some code to specify the preferred window. In my own app, I take the global frame rectangles for each screen and then scale them way down into a NSView to display something that looks like the Monitors pane from System Preferences:

Set Video Preferences

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]

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)

}
}

How to open new window on second screen (second display)

After a bit of tinkering around I came across this solution which I modified to make it work for me. Add the following extension.

extension View {
private func newWindowInternal(with title: String) -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 0, height: 0),
styleMask: [.closable, .borderless],
backing: .buffered,
defer: false)

guard let secondScreen = NSScreen.screens.last else {
print("Failed to find last display")
return window
}

window.setFrame(secondScreen.frame, display: true)
window.level = NSWindow.Level.screenSaver
window.isReleasedWhenClosed = false
window.title = title
window.orderFront(nil)
return window
}

func openNewWindow(with title: String = "new Window") {
self.newWindowInternal(with: title).contentView = NSHostingView(rootView: self)
}
}

In your ContentView, add a button which will trigger the activation of the new window. Here's an example ContentView

struct ContentView: View {
@State private var windowOpened = false

var body: some View {
VStack {
Button("Open window") {
if !windowOpened {
ProjectorView(isOpen: $windowOpened).openNewWindow(with: "Projector")
}
}
.keyboardShortcut("o", modifiers: [.option, .command])

Button("Close window") {
NSApplication.shared.windows.first(where: { $0.title == "Projector" })?.close()
}
.keyboardShortcut("w", modifiers: [.option, .command])
}
.frame(width: 300, height: 100)
}
}

Finally, here's what ProjectorView looks like.

struct ProjectorView: View {
@Binding var isOpen: Bool

var body: some View {
HStack {
Spacer()
VStack {
Spacer()
Text("Hello World!")
.font(.title2)
Spacer()
}
Spacer()
}
.padding()
.onDisappear {
isOpen = false
}
.onAppear {
isOpen = true
}
}
}

This solution works great. Pressing O will open ProjectorView on the second screen above all other windows and pressing W will close ProjectorView window. Window settings, and window level can be tweaked in the extension.

Tested on macOS Monterey, Xcode 13, Apple M1 MacBook Air.

How to change the NSScreen a NSWindow appears on

I could not get toohtik's answer to work. What I ended up doing was subclassing NSWindow and then overriding constrainFrameRect: toScreen:. This will automatically open the new window on the "main screen" of the application.

- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
{
AppDelegate *delegate = [[NSApplication sharedApplication] delegate];
return [super constrainFrameRect:frameRect toScreen:delegate.window.screen];
}

Find which monitor or screen contains mouse pointer - swift, macos

Answering my own question since I found the answer - this might be helpful to someone else.

It's not too difficult to find which screen/monitor the mouse is on, but you do have to iterate through each screen in order to do so.

Swift 4

func getScreenWithMouse() -> NSScreen? {
let mouseLocation = NSEvent.mouseLocation
let screens = NSScreen.screens
let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) })

return screenWithMouse
}

Objective C

A similar way to get the same result in Objective-C would be:

NSPoint mouseLoc = [NSEvent mouseLocation];
NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
NSScreen screen;
while ((screen = [screenEnum nextObject]) && !NSMouseInRect(mouseLoc,
[screen frame], NO));

Enable fullscreen for floating window in macOS app

I got it to work by setting collectionBehavior:

NSApplication.shared.windows.forEach { window in
window.collectionBehavior = [.fullScreenPrimary]
window.level = .floating
}

I found a similar SO question, they also set some properties (not the level) of the window, and fullscreen gets disabled.

So is it that if collectionBehavior is not specified for an NSWindow, then fullscreen is enabled as long as certain NSWindow properties (e.g. level) are not explicitly set?

how to move NSWindow to a particular screen?

[NSScreen screens] gives you an array of NSScreens. The screen at index 0 is the one that's got your menu on.

So pick the other screen from the array, find it's visibleFrame and change the frame of your window to go inside it.



Related Topics



Leave a reply



Submit