【SwiftUI】MenuとContextMenuについて

# はじめに
SwiftUIの`Menu`について、公式ドキュメントを読んだ内容をまとめてみました。

https://developer.apple.com/documentation/swiftui/menus-and-commands

# 環境
Xcode 14.0

# 内容
## `Menu`

https://developer.apple.com/documentation/swiftui/menu

#### [`init(_:content:)`](https://developer.apple.com/documentation/swiftui/menu/init(_:content:)-7v768)
`Menu`を使うことで、メニューボタンとメニューオプションを表示することができる
`Menu`の中には、さらに`Menu`を入れることも可能

```swift
Menu("Actions") {
    Button("Duplicate", action: duplicate)
    Button("Rename", action: rename)
    Button("Delete…", action: delete)
    Menu("Copy") {
        Button("Copy", action: copy)
        Button("Copy Formatted", action: copyFormatted)
        Button("Copy Library Path", action: copyPath)
    }
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/d937f08f-b765-4ca9-0ff1-d7a7c836d960.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/5d1e6dff-40c7-ffab-175c-09e3bdf336e9.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/4e5f7d13-5eb2-e26c-9322-1831b3024473.png">

#### [`init(content:label:)`](https://developer.apple.com/documentation/swiftui/menu/init(content:label:))
`Label`を使うこともできる

```swift
Menu {
    Button("Open in Preview", action: openInPreview)
    Button("Save as PDF", action: saveAsPDF)
} label: {
    Label("PDF", systemImage: "doc.fill")
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/5e61219a-1b64-59b1-4c5d-958e391926c7.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/f21a113a-edba-3d06-b22d-e19c40281afd.png">

#### [`init(content:label:primaryAction:)`](https://developer.apple.com/documentation/swiftui/menu/init(content:label:primaryaction:))
`primaryAction`を定義することで、メニューボタンをタップで`primaryAction`の実行、メニューボタンを長押しでメニューオプションが表示されるようになる

```swift
Menu {
    Button(action: addCurrentTabToReadingList) {
        Label("Add to Reading List", systemImage: "eyeglasses")
    }
    Button(action: bookmarkAll) {
        Label("Add Bookmarks for All Tabs", systemImage: "book")
    }
    Button(action: show) {
        Label("Show All Bookmarks", systemImage: "books.vertical")
    }
} label: {
    Label("Add Bookmark", systemImage: "book")
} primaryAction: {
    addBookmark()
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/40b3a55e-1dc6-1c3f-76e3-0d8d34f76eaf.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/4b15af22-6ac5-1916-6003-879886bc3f7f.png">

## `MenuOrder`

https://developer.apple.com/documentation/swiftui/menuorder

#### [`menuOrder(_:)`](https://developer.apple.com/documentation/swiftui/view/menuorder(_:))

Viewから表示されるMenuの順序を指定する

- [`automatic`](https://developer.apple.com/documentation/swiftui/menustyle/automatic)
システムが選択したメニューの順序
- [`fixed`](https://developer.apple.com/documentation/swiftui/menuorder/fixed)
上から下へ順番に並べる
- [`priority`](https://developer.apple.com/documentation/swiftui/menuorder/priority)
ユーザーのインタラクションポイントに最も近い最初の項目を保持する

## `ContextMenu`
#### [`contextMenu(menuItems:preview:)`](https://developer.apple.com/documentation/swiftui/view/contextmenu(menuitems:))

Viewにコンテキストメニューを追加する

```swift
Text("Turtle Rock")
    .padding()
    .contextMenu {
        Button {
            // Add this item to a list of favorites.
        } label: {
            Label("Add to Favorites", systemImage: "heart")
        }
        Button {
            // Open Maps and center it on this item.
        } label: {
            Label("Show in Maps", systemImage: "mappin")
        }
    }
```
テキストを長押しすることでメニューボタンが表示される
<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/28f25e39-09ab-dcf8-e712-65b891aba8f4.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/b9783a0e-ae86-6241-051d-0a768ff69658.png">

#### [`contextMenu(menuItems:preview:)`](https://developer.apple.com/documentation/swiftui/view/contextmenu(menuitems:preview:))

Viewにプレビュー付きコンテキストメニューを追加する

```swift
Text("Questionmark")
    .padding()
    .contextMenu {
        Button {
            // Add this item to a list of favorites.
        } label: {
            Label("Add to Favorites", systemImage: "heart")
        }
        Button {
            // Open Maps and center it on this item.
        } label: {
            Label("Show in Maps", systemImage: "mappin")
        }
    } preview: {
        Image(systemName: "questionmark.square.dashed")
            .resizable()
            .frame(width: 200, height: 200)
    }
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/6ba85640-51a2-087a-443f-e98f54f79a46.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/2cf6c8bd-9bfc-9992-74df-a7ceca43359f.png">

#### [`contextMenu(forSelectionType:menu:primaryAction:)`](https://developer.apple.com/documentation/swiftui/view/contextmenu(forselectiontype:menu:primaryaction:))

Viewにアイテムベースのコンテキストメニューを追加する

```swift
struct ContextMenuItemExample: View {
    var items: [Item]
    @State private var selection = Set<Item.ID>()

    var body: some View {
        List(selection: $selection) {
            ForEach(items) { item in
                Text(item.name)
            }
        }
        .contextMenu(forSelectionType: Item.ID.self) { items in
            if items.isEmpty { // Empty area menu.
                Button("New Item") { }

            } else if items.count == 1 { // Single item menu.
                Button("Copy") { }
                Button("Delete", role: .destructive) { }

            } else { // Multi-item menu.
                Button("Copy") { }
                Button("New Folder With Selection") { }
                Button("Delete Selected", role: .destructive) { }
            }
        }
    }
}

struct Item: Identifiable {
    let id: Int
    let name: String
}
```

- リスト外を長押し
- アイテムを一つ長押し
- アイテムを複数選択して長押し

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/872a17b7-24bb-9916-a3c2-8b34f9ebf398.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/78e5cd30-e2c5-72cc-0b42-b51a025c1635.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/134f43b0-d978-e61c-4d8e-d9c20972f107.png">

# おわりに
`Menu`はよく使っていたましたが、`primaryAction`を定義できることは知らなかったので、今回ドキュメントを読んだことで収穫がありました。

# 参考
https://developer.apple.com/documentation/swiftui/menus-and-commands

https://swiftontap.com/menu