🦋
SwiftUI: NSHostingViewでもAppDelegateをEnvironmentObjectで渡したい
まず、SwiftUI AppでAppDelegateを使う方法は以下のような感じ。
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
var body: some Scene {
// 省略
}
}
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
func applicationDidFinishLaunching(_ notification: Notification) {}
}
次にViewからAppDelegateのもつプロパティーにアクセスしたい場合は以下のような感じになる。
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
var body: some Scene {
WindowGroup {
ContentView()
+ .environmentObject(appDelegate)
}
}
}
class AppDelegate: NSObject: NSApplicationDelegate, ObservableObject {
+ @Published var showProgress: Bool = false
func applicationDidFinishLaunching(_ notification: Notification) {}
}
struct ContentView: View {
+ @EnvironmentObject private var appDelegate: AppDelegate
var body: some View {
ProgressView()
.progressViewStyle(.circular)
+ .opacity(appDelegate.showProgress ? 1.0 : 0.0)
}
}
本題
このままではNSHostingView
だと@EnvironmentObject
ではアクセスできない。
なので、Shared InstanceをAppDelegateに生やして、NSHostingViewを初期化するタイミングで.environmentObject()
してあげる。
class AppDelegate: NSObject: NSApplicationDelegate, ObservableObject {
+ static private(set) var shared: AppDelegate! = nil
@Published var showProgress: Bool = false
func applicationDidFinishLaunching(_ notification: Notification) {
+ AppDelegate.shared = self
}
}
class SampleWindow: NSWindow {
init(frame: NSRect) {
super.init(
contentRect: frame,
styleMask: [.closable, .miniaturizable, .resizable, .fullSizeContentView, .titled],
backing: .buffered,
defer: true
)
let sampleView = SampleView()
+ .environmentObject(AppDelegate.shared)
self.contentView = NSHostingView(rootView: sampleView)
}
}
Discussion
ObservableObject
なViewModelからAppDelegate
のプロパティにアクセスしたい場合は以下。この場合、事前に
.environmentObject(AppDelegate.shared)
をしておく必要はない。