UITextViewで独自のEdit Menuを表示する
実行環境
- Xcode 14.2
- iOS 16
iPhoneのメモアプリで、テキストを長押しすると出てくるメニュー。
あれを自分で作るにはどうすればいいのかを調べてみました。
そもそもあのメニューの名前はなんだろうと調べたところ、Human Interface GuidelinesにはEdit Menuという名前で紹介されていました。
Edit Menuの表示
さて、このEdit Menu、UITextView
ならデフォルトで表示されるようです。
ということでSwiftUIで表示するサンプルコードを書いてみました。
struct TextEditor: UIViewRepresentable {
typealias UIViewType = UITextView
typealias Coordinator = TextEditCoordinator
@Binding var editText: String
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.isSelectable = true
textView.isEditable = true
textView.text = editText
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {}
}
UIViewRepresentable
でUITextView
をSwiftUIで表示するコードです。
makeUIView
メソッドでUITextView
を作成します。
isSelectable
とisEditable
をtrueにすることで、選択可能で編集可能なテキストビューを作成します。
これだけでテキストを長押しするとEdit Menuが表示されるテキストビューが作成可能です。
独自のメニューを表示
デフォルトのEdit Menuでは以下の項目が表示されます。
- Cut
- 選択文字をカットする
- Copy
- 選択文字をコピーする
- Replace...
- 選択文字を置き換える
- Look Up
- 選択文字を調べる
- Translate
- 選択文字を翻訳する
- Search Web
- 選択文字をWebで検索する
- Share...
- 選択文字を共有する
自分で独自のメニューを表示したい場合はどうすればいいでしょうか?
iOS 16からUITextViewDelegate
のtextView(_:editMenuForTextIn:suggestedActions:)を使うとハンドリングできます。
struct TextEditor: UIViewRepresentable {
func makeCoordinator() -> Coordinator {
return TextEditCoordinator()
}
}
final class TextEditCoordinator: NSObject, UITextViewDelegate {
func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? {
let customMenu = UIMenu(
title: "uimenue",
options: .displayInline,
children: [
UIAction(title: "menuItem1") { _ in
print("menuItem1")
},
UIAction(title: "menuItem2") { _ in
print("menuItem2")
},
UIAction(title: "menuItem3") { _ in
print("menuItem3")
}
]
)
return UIMenu(children: suggestedActions + [customMenu])
}
}
TextEditor
にmakeCoordinator
を追加してUITextViewDelegate
を準拠したクラスTextEditCoordinator
を返すようにします。
TextEditCoordinator
でtextView(_:editMenuForTextIn:suggestedActions:)
を実装します。
suggestedActions
に標準のEdit Menuのデータが入っています。
独自のUIMenu
としてcustomMenu
を作成し、suggestedActions
を含めて値を返せば、標準のEdit Menuに追加で独自のメニューを表示することができます。
UIMenu
を作成する際のoptions
パラメーターは主にdisplayInline
とdestructive
の2つがあります。違いを見ていきましょう。
displayInline
displayInline
はインラインでメニューを表示する方式です。
children
で作成したUIAction
が横並びで表示されます。
キャプチャではmenuItem1
が見えていますが、メニューを横にスライドするとmenuItem2
とmenuItem3
が見えてきます。
タップするとそれぞれのUIAction
のクロージャーが呼ばれます。
destructive
destructive
はメニューを階層的に見せる方式です。
UIMenu
のtitle
パラメーターの名前の親メニューが表示され、タップするとchildren
で作成したUIAction
がメニューとして表示されます。
ここのuimenue
をタップすると、
menuItem1
、menuItem2
、menuItem3
が表示されます。
サンプルコード
サンプルコードをGistに上げました。参考にしてください。
宣伝
BOOTHより、同人版「Swift Concurrency入門」発売中です。
Swift Concurrencyを網羅的に学べ、さらに既存アプリへの適応方法も解説しています。
日本語で体系的に学べる解説本は他になかなかありません。
1章、2章が立ち読みできるおためし版もありますので、ぜひチェックしてください!