🦋
SwiftUI: macOSのSettingsウインドウでTabViewを指定して開く
要点
- SwiftUI製アプリで
AppDelegate
が使えるように下準備をする -
AppDelegate
がObservableObject
に準拠するようにする - SettingsのViewで
AppDelegate
を@EnvironmentObject
で参照するようにする - Tabの選択状態を覚えておく
selection
をAppDelegate
で管理するようにする
SampleApp.swift
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
Settings {
SettingsView()
}
}
}
AppDelegate.swift
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
@Published var settingsTab: SettingsView.Tabs = .general
// 環境設定のウインドウを返す
private var settingsWindow: NSWindow? {
return NSApp.windows.first(where: { window in
window.frameAutosaveName == "com_apple_SwiftUI_Settings_window"
})
}
func applicationDidFinishLaunching(_ notification: Notification) {}
// AppDelegateから環境設定を開く導線
func openPreferences(tab: SettingsView.Tabs) {
settingsTab = tab
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
guard let window = settingsWindow else { return }
if window.canBecomeMain {
window.orderFrontRegardless()
window.center()
NSApp.activate(ignoringOtherApps: true)
}
}
}
SettingsView.swift
struct SettingsView: View {
@EnvironmentObject private var appDelegate: AppDelegate
enum Tabs: Hashable {
case general
case accounts
}
var body: some View {
TabView(selection: $appDelegate.settingsTab) {
GeneralSettingsView() // 別途用意
.tabItem {
Label("general", systemImage: "gear")
}
.tag(Tabs.general)
AccountsSettingsView() // 別途用意
.tabItem {
Label("accounts", systemImage: "person.fill")
}
.tag(Tabs.accounts)
}
.padding(.horizontal, 40)
.padding(.vertical, 20)
}
}
Discussion
何気に、環境設定のウインドウかどうかを判断する方法はテクい。
環境設定を開いている状態でViewのHierarchyを見てNSWindow.FrameAutosaveNameという値が特定のものになることに気づいた。