Xcode Version 16.1 (16B40) を使用しています。
サンプルの準備
新規プロジェクト → macOS → App でプロジェクトを作り ContentView の中身を移動してファイルを1つにします。
サンプルの実行と変更
Xcode で Command+R でコンパイルして実行する
メニューバーの "File" には新規タブで開く項目はありません
メニューバーの "View" を開きます
タブに関する項目が出てきました。
"Show All Tabs" を突いてみる
新規でタブが足せる…
"Show Tabs Bar" を突いてみる
こっちでも右上の "+" でタブが足せる…
タブ機能を有効活用するなら "File" に "New Tab" があった方がよさそうですが、デフォルトでは用意されてません。
"File" に "New Tab" を足す
"New Window" は openWindow で開く方法がありますが、これに準じる方法で "New Tab" する方法を見つけることができませんでした。NSWindow に ContentView を割り当てる方法が彼方此方にありますが、それだと App の body 定義が使用されません。メニューバーの "View" 操作経由のタブ追加では body 定義が有効です。
試しに openWindow や NSApplicationDelegate.applicationOpenUntitledFile で作った新規ウインドウをタブ グループに追加したら想定した動作になりました。
SampleApp.swift
import SwiftUI
@main
struct SampleApp: App {
@Environment(\.openWindow) private var openWindow
var body: some Scene {
WindowGroup(id: "hello-world") {
ContentView()
}
.commands {
// メニュー バーの "File" に "New Tab" を追加
// ショートカットに Command+T を設定
CommandGroup(before: .newItem) {
Button("New Tab") {
openNewTab()
}
.keyboardShortcut("T", modifiers: [.command])
}
}
}
// 新規タブを開く
private func openNewTab() {
guard let keyWindow = NSApp.keyWindow else { return }
guard let tabGroup = keyWindow.tabGroup else { return }
#if true
guard let appDelegate = NSApp.delegate else { return }
guard let openUntitledFile = appDelegate.applicationOpenUntitledFile else { return }
if !openUntitledFile(NSApp) { return }
#else
openWindow(id: "hello-world")
#endif
// 追加される新規ウインドウは最後に存在すると推測
guard let window = NSApplication.shared.windows.last else { return }
// そのままだと独立ウインドウで現れるので tabGroup への追加を試みる
tabGroup.addWindow(window)
}
}
struct ContentView: View {
static private var viewIndex: Int = 1
static private var viewCount: Int = 0
@State private var viewTitle = "Hello, World! (\(ContentView.viewIndex))"
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text(viewTitle)
}
.navigationTitle(viewTitle)
.padding()
.onAppear {
ContentView.viewIndex += 1
ContentView.viewCount += 1
}
.onDisappear {
ContentView.viewCount -= 1
if ContentView.viewCount == 0 {
// 全てのウインドウを閉じたら終了
NSApplication.shared.terminate(nil)
}
}
}
}
#Preview {
ContentView()
}
こんな方法でいいのか甚だ疑問だけど...