🏷️

UIEditMenuInteractionのメニューをカスタマイズする

2022/09/03に公開

iOS16からUIMenuControllerが非推奨になり、UIEditMenuInteractionを使うことになりました。

使い方

次のように、メニューを表示したいビューにUIEditMenuInteractionをアタッチします。

editMenuInteraction = UIEditMenuInteraction(delegate: self)
view.addInteraction(editMenuInteraction!)

メニューを表示するときは、UIEditMenuInteractionpresentEditMenu(with:)を呼びます

let configuration = UIEditMenuConfiguration(
  identifier: "dev.noppe.app.editMenu.longpress",
  sourcePoint: point
)
editMenuInteraction?.presentEditMenu(with: configuration)

メニューの構築はUIEditMenuInteractionDelegateを利用します。

public func editMenuInteraction(_ interaction: UIEditMenuInteraction, menuFor configuration: UIEditMenuConfiguration, suggestedActions: [UIMenuElement]) -> UIMenu? {
 // 表示したいメニューを返す
}

同じ画面で複数のメニューを出し分けたい場合は、configuration.identifierを参照することでメニューの構築を分岐することができます。

メニューのカスタマイズ

標準メニューだけを表示する

標準メニューとはコピーや切り取りといったメニューです。
これらを表示したい場合は、特に設定する必要はありません。
UIEditMenuInteractionDelegateでnilを返しても標準メニューだけを返すことができます。

自作のメニューだけを表示する

UIEditMenuInteractionDelegateで、自作のUIMenuを作って返せば良いです。

return UIMenu(children: [
  UIAction(title: "反転", handler: { _ in ... })
])

自作のメニューと標準メニューのどちらも出す

UIEditMenuInteractionDelegatesuggestedActionsに標準メニューが含まれているので、自作のメニューの中で追加してあげます。

return UIMenu(children: [
  UIAction(title: "反転", handler: { _ in ... })
] + suggestedActions)

標準メニューの一部を出す

これがちょっとややこしいです。
NGパターンとしては、suggestedActionsのtitleなどを参照して取り出すのはローカライズなどに対応できないため正しくありません

// NG
return UIMenu(children: [
  UIAction(title: "反転", handler: { _ in ... })
] + suggestedActions.filter { $0.title == "コピー" })

ではどうするかというと、UIMenuController同様にUIRespondercanPerformActionで制御します。

public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
  action == #selector(UIResponderStandardEditActions.copy)
}

これでメニューを絞ることができました。
UIMenuControllerと異なり、ここでfalseが返却されても追加した自作メニューは影響を受けません。

Discussion