目次
概要
LINEのユーザー一覧のセルを長押しするとメニューを表示することができる。
加えて、UIのタップ処理とは別に機能を持たせたい時、ロングタップは良く使われるが、
そのロングタップの動作でも複数の機能を持たせたい時がある。
そんな時は長押しした時にメニューを表示させて、複数の操作ができるようにしよう。
メニューを表示した方がユーザーからもわかりやすくなるはず。
ソースコード
アプリの最初の実行部分
TestApp.swift
import SwiftUI
@main
struct TestApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ListMenuView()
}
}
}
SwiftView表示部分
ListMenuView.swift
import SwiftUI
struct ListMenuView: View {
@ObservedObject private var viewModel: ListMenuViewModel = ListMenuViewModel()
var body: some View {
NavigationView {
VStack {
listView()
}
.navigationTitle("リストの備忘録動作確認")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
listOrderMenu()
}
}
}
}
private func listView() -> some View {
return List {
ForEach(Array(viewModel.dataList.enumerated()), id: \.offset) { index, data in
Button {
print("\(data)をタップしました。")
} label: {
Text(data)
}
.contextMenu(menuItems: {
Button {
print("編集する?")
} label: {
Text("編集")
}
Button {
viewModel.deleteData(index: index)
} label: {
Text("削除")
.foregroundColor(.red)
}
})
}
}
}
private func listOrderMenu() -> some View {
return Menu {
ForEach(DataOrder.allCases, id:\.self) { dataOrder in
Button {
viewModel.sortDataOrder(selectedOrder: dataOrder)
} label: {
HStack {
if dataOrder == viewModel.selectedOrder {
Image(systemName: "checkmark")
}
Text(dataOrder.rawValue)
}
}
}
} label: {
Image(systemName: "arrow.up.and.down.text.horizontal")
}
}
}
Swift処理部分
ListMenuViewModel.swift
import Foundation
enum DataOrder: String, CaseIterable, Identifiable {
case titleAscendingOrder = "項目名の昇順"
case titleDescendingOrder = "項目名の降順"
var id: String { rawValue }
}
class ListMenuViewModel: ObservableObject {
@Published private(set) var dataList: [String]
@Published private(set) var selectedOrder: DataOrder = .titleAscendingOrder
init() {
dataList = []
for index in 0 ..< 20 {
dataList.append("データ\(index)")
}
}
func sortDataOrder(selectedOrder: DataOrder) {
self.selectedOrder = selectedOrder
switch selectedOrder {
case .titleAscendingOrder:
dataList = dataList.sorted(by: {$0 < $1})
case .titleDescendingOrder:
dataList = dataList.sorted(by: {$1 < $0})
}
}
func deleteData(index: Int) {
self.dataList.remove(at: index)
}
}
Swiftスクリーンショット
詳細
長押しした時に表示されるメニュー
こちらの実装部分は以下のような処理で行える
.contextMenu(menuItems: {
// ここにメニューとして表示させたいUIを記載していく
})
ここに今回の例のようにButtonを羅列していくと動画のようなメニューを表示することができる。
ただし、この時タップした項目はNavigationLinkによる画面遷移ができない様子。(要調査)
代わりに、フラグやデータの編集はできるため、以下のことは実装可能
- ダイアログを表示する
- 格納しているデータを編集して表を更新する(削除なども可能)
アイコンをタップした時に表示されるメニュー
こちらの実装はMenuを使用する。Menuの書き方の一つとして以下のように書く。
Menu {
// 表示したいUIを羅列する
} label: {
// View上の表示を設定する
}
今回の例では、
「表示したいUI」の部分にメニューの内容と各々をタップした時の処理を、
「View上の表示」の部分にボタンのアイコンを書いている。
また、Pickerでメニューを表示することもできるが、View上の表示が選択したメニューの内容になる。これはXcode上のバグと言われているが、これを解決する手段としてMenuで実装するというのが挙げられる。
また、Menuの場合はそのままではどれを選択しているかわからないので、以下のようにチェックマークを入れることで現在の設定がわかるようにしている。
HStack {
if dataOrder == viewModel.selectedOrder {
Image(systemName: "checkmark")
}
Text(dataOrder.rawValue)
}
参考ページ:
Qiita「SwiftUIのドロップダウン、ホイール、メニュー、Picker」
Qiita「【SwiftUI】長押しでメニューを表示する」
カピ通信「【SwiftUI】Menuの使い方」