Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI macos NSWindow instance

Using xcode 12.3 and swift 5.3 with the SwiftUI App Life cycle to build a macOS application, what is the best way to access and change the appearance and behaviour of the NSWindow?

Edit: What I'm really after is the NSWindow instance.

I've added an AppDelegate, but as I understand it the NSWindow is likely to be nil, so unavailable for modification, and simply creating one here similar to the AppKit App Delegate Life cycle method results in two windows appearing at launch.

One solution would be preventing the default window from appearing, and leaving it all to the applicationDidFinishLaunching method, but not sure this is possible or sensible.

The WindowStyle protocol looks to be a possible solution, but not sure how best to leverage that with a CustomWindowStyle at this stage, and whether that provides access to the NSWindow instance for fine-grained control.

class AppDelegate: NSObject, NSApplicationDelegate {        
    func applicationDidFinishLaunching(_ aNotification: Notification) {
      // In AppKit simply create the NSWindow and modify style.
      // In SwiftUI creating an NSWindow and styling results in 2 windows, 
      // one styled and the other default.
    }
}

@main
struct testApp: App {
    
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate : AppDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
like image 608
hillmark Avatar asked Dec 20 '20 11:12

hillmark


People also ask

How do I open a new window in SwiftUI on macOS?

Here is how to open a new window in SwiftUI on macOS. Text ( "Hello, world!") In your App add another WindowGroup for your viewer and set it to enable handling of external launch events (an internal event in our case).

What is the function of NSWindow?

A window that an app displays on the screen. A single NSWindow object corresponds to, at most, one on-screen window. Windows perform two principal functions: To accept and distribute mouse and keyboard events the user generates to the appropriate views

How to create a search bar in SwiftUI for macOS?

There’s no search bar in SwiftUI for macOS, only TextField. The native macOS search bar has a default magnifying glass icon and a clear button, which will take us some time to replicate it in pure SwiftUI. Or we need to use NSViewRepresentable to wrap NSSearchField.

What does SwiftUI mean?

SwiftUI makes it easy to declare toolbar and items. While there is less options for placement on Mac than there is in iOS or Catalyst, the .navigation placement works OK.


1 Answers

Although I am not entirely sure this is exactly the right approach, based on the answer to this question: https://stackoverflow.com/a/63439982/792406 I have been able to access the NSWindow instance and modify its appearance.

For quick reference, here's a working example based on the original code provided by Asperi using xcode 12.3, swift 5.3, and the SwiftUI App Life cycle.

@main
struct testApp: App {    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class Store {
    var window: NSWindow
    
    init(window: NSWindow) {
        self.window = window
        self.window.isOpaque = false
        self.window.backgroundColor = NSColor.clear
    }
}

struct ContentView: View {
    @State private var window: NSWindow?
    var body: some View {
        VStack {
            Text("Loading...")
            if nil != window {
                MainView(store: Store(window: window!))
            }
        }.background(WindowAccessor(window: $window))
    }
}

struct MainView: View {

    let store: Store
    
    var body: some View {
        VStack {
            Text("MainView with Window: \(store.window)")
        }.frame(width: 400, height: 400)
    }
}

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            self.window = view.window
        }
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {}
}
like image 153
hillmark Avatar answered Sep 19 '22 23:09

hillmark