Search code examples
macosswiftuiappkit

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


The task is simple, but I don't know where to start looking for any pointers on MacOs App development with two displays. I'm building a presenter app where the main app will be displaying on the primary display and with the push of a button I need to push a fullscreen window onto the second display or second monitor. How would I go about doing this? I am using SwiftUI, but am open to other suggestions.


Solution

  • 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.