问题
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()
}
}
}
回答1:
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) {}
}
回答2:
Interesting approach @hillmark.
I also got it working with an approach using NSApplicationDelegateAdaptor
.
I believe the code below would only help you with a single window MacOS SwiftUI application - a multi window app would likely need to handle all the windows, rather than just take the first one.
I'm also not sure of any caveats of my approach yet.
For now this solves my issue precisely, but I'll check your approach out also.
import SwiftUI
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
if let window = NSApplication.shared.windows.first {
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.isOpaque = false
window.backgroundColor = NSColor.clear
}
}
}
@main
struct AlreadyMacOSApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
NewLayoutTest()
}
}
}
来源:https://stackoverflow.com/questions/65379307/swiftui-macos-nswindow-instance