[SwiftUI][macOS] SwiftUI でのメニューの扱い方

SwiftUI2021

SwiftUI (特に macOS)での メニューの扱い方を説明します

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 5
  • Xcode 13 beta5
  • iOS 15 beta

メニュー

Apple の HIG によると、以下の3種類のメニューがあります。

  • Menu bar menu
  • Contextual menu
  • Dock menu

この記事では、Menu bar menu について説明していきます。

メニューの種類

メニューは、以下のような構成になっています。(アプリによっては存在しないメニューがあります)

  • Apple menu
  • App menu
  • File menu
  • Edit menu
  • Format menu
  • View menu
  • App-specific menu(s)
  • Window menu
  • Help menu

Xcode のメニュー構成

ちなみに、Xcode は、以下のようなメニュー構成です。

XcodeMenu
XcodeMenu

Format menu が存在せず、Find から Source Control までが、App-specific menu(s) にあたります。

SwiftUI でのメニュー構成

App テンプレートでのメニュー構成(1: non-document based)

macOS の Non-Document な App のテンプレートで作成したアプリでは、以下のメニューのみ存在します。

  • Apple menu
  • App menu
  • File menu
  • Edit menu
  • Window menu
  • Help menu

App テンプレートでのメニュー構成(2: document based)

macOS の Document base な App のテンプレートで作成したアプリでは、以下のメニューのみ存在します。

  • Apple menu
  • App menu
  • File menu
  • Edit menu
  • Window menu
  • Help menu

よくみるとサブメニューの中身は、アプリのタイプによって異なるものになっていますが、メニューバー項目としては、上記のようになっています。

メニューを追加する

テンプレートで設定されているメニューに 自分のメニューを追加する方法を説明します。

自分のメニューを表示する

.commands

SwiftUI では、Scene に .commands を使って付与することになります。
Apple のドキュメントは、こちら

.commands は、macOS, iPadOS それぞれに応じた処理を行ってくれます。(ここでは、macOS についてのみ説明します)

CommandMenu

CommandMenu を使うことで、menu bar 上のメニューを作成することができます。

実際のメニュー項目は、View で作成します。

以下は、Menu bar 項目は、"MyMenu" で メニュー項目は、"MyCommand1" というメニューを作成するコードです。


//
//  macOSAppApp.swift
//
//  Created by : Tomoaki Yagishita on 2021/09/09
//  © 2021  SmallDeskSoftware
//

import SwiftUI

@main
struct macOSAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .commands {
            // (1)
            CommandMenu("MyMenu") {
                MyCommand1()
            }
        }
    }
}

struct MyCommand1: View {
    var body: some View {
        Button(action: {
            print("MyCommand1")
        }, label: {
            // (2)
            Text("MyCommand1")
        })
            // (3)
            .keyboardShortcut("1", modifiers: [.shift, .command])
    }
}
コード解説
  1. Menu bar に "MyMenu" というメニューを指定しています
  2. メニューアイテムは、"MyCommand1" として表示されます
  3. ショートカットとして、Shift+Command+1 を設定しています。

コマンドの処理

上記のコマンドは、Button で実装していますので、action: にコマンド処理を記述します。

サンプルのコードでは、MyCommand1 と コンソールに print する処理を記述しています。

複数のメニュー

.commands は、ViewBuilder のようなメソッドのため、複数のコマンドを記述することができます。
実際には、CommandsBuilder というものです。
Apple のドキュメントは、こちら
# 最大10要素までしか記述できないところも、ViewBuilder と同じです。

この機能を使うことで、.commands 内に 複数の CommandMenu を記述することができます。


        .commands {
            // (1)
            CommandMenu("MyMenu") {
                MyCommand1()
            }
            CommandMenu("MyMenu2") {
                MyCommand2()
            }
        }

メニューは、App-specific menu の場所に追加されるため、位置としては、Edit と Window の間に配置されます。

既存メニューへの追加

アプリケーションによっては、既存メニューの中に(例えば File メニュー)、App-specific なメニューを追加したい時があります。

以下は、既存メニューの中に 追加する方法です。

メニュー要素

メニュー要素自体は、同じ作りです。(ので省略です)

位置を指定したメニュー追加

CommandGroup を使用して、メニュー要素を指定した位置に配置します。
Apple のドキュメントは、こちら

CommandGroup(after: ) を使用することで、指定要素の後に メニュー要素を配置します。

同様に、CommandGroup(before:) を使用すると 指定要素の前に、CommandGroup(replace:) を使用すると 指定要素と置き換えて メニュー要素が配置されることになります。

例として作成した MyCommand1 というメニュー要素を、"About App" の後に追加してみます。


//
//  macOSAppApp.swift
//
//  Created by : Tomoaki Yagishita on 2021/09/09
//  © 2021  SmallDeskSoftware
//

import SwiftUI

@main
struct macOSAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .commands {
            CommandMenu("MyMenu") {
                MyCommand1()
            }
            CommandMenu("MyMenu2") {
                MyCommand2()
            }
            CommandGroup(after: .appInfo) {
                MyCommand1()
            }
        }
    }
}

// ommit MyCommand1, MyCommand2

配置時に参照できるメニュー

配置時に参照・置換できるメニューは以下の通りです。

  • appInfo
  • appSettings
  • systemServices
  • appVisibility
  • appTermination
  • newItem
  • saveItem
  • importExport
  • printItem
  • undoRedo
  • pasteboard
  • textEditing
  • textFormatting
  • toolbar
  • sidebar
  • windowSize
  • windowList
  • windowArrangement
  • help

Apple のドキュメントは、こちら

用意されている CommandGroup

SwiftUI ではいくつかのメニューセットが用意されています。

  • SidebarCommands
  • TextEditingCommands
  • TextFormattingCommands
  • ToolbarCommands
  • ImportFromDevicesCommands
  • EmptyCommands

.commands を使って、上記 CommandGroup を設定することで、それぞれのメニューが表示されるようになります。

# EmptyCommands の使用目的は、ちょっとわかりません・・・・ 設定してもメニューは追加されません。

まとめ:SwiftUI でのコマンドの扱い方

SwiftUI でのコマンドの扱い方
  • Scene に .commands で設定する
  • CommandMenu がメニュー項目になる
  • CommandGroup を使用すると既存メニューに追加したり、置き換えることができる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です