🦋

SwiftUI: macOSのSettingsウインドウでTabViewを指定して開く

2022/11/04に公開約2,000字1件のコメント

要点

  • SwiftUI製アプリでAppDelegateが使えるように下準備をする
  • AppDelegateObservableObjectに準拠するようにする
  • SettingsのViewでAppDelegate@EnvironmentObjectで参照するようにする
  • Tabの選択状態を覚えておくselectionAppDelegateで管理するようにする
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

何気に、環境設定のウインドウかどうかを判断する方法はテクい。

private var settingsWindow: NSWindow? {
    return NSApp.windows.first(where: { window in
        window.frameAutosaveName == "com_apple_SwiftUI_Settings_window"
    })
}

環境設定を開いている状態でViewのHierarchyを見てNSWindow.FrameAutosaveNameという値が特定のものになることに気づいた。

ログインするとコメントできます