There is no way to programmatically trigger a context menu, but with some simple bookkeeping you can prevent it from showing when not wanted (e.g when touches are active on your responder).
To hide the preview, just return nil from previewProvider
in UIContextMenuConfiguration's initializer.
Here is a complete implementation with a view controller's view as the target:
import UIKit
class ViewController: UIViewController {
var touchesInSession = false
override func viewDidLoad() {
super.viewDidLoad()
let interaction = UIContextMenuInteraction(delegate: self)
view.addInteraction(interaction)
let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressHandler))
view.addGestureRecognizer(recognizer)
}
@objc func longPressHandler(recognizer: UILongPressGestureRecognizer) {
guard recognizer.state == .began else { return }
presentMenu(from: recognizer.location(in: view))
}
func presentMenu(from location: CGPoint) {
view.becomeFirstResponder()
let saveMenuItem = UIMenuItem(title: "New Folder", action: #selector(createFolder))
let deleteMenuItem = UIMenuItem(title: "Get Info", action: #selector(getInfo))
UIMenuController.shared.menuItems = [saveMenuItem, deleteMenuItem]
UIMenuController.shared.showMenu(from: view, rect: .init(origin: location, size: .zero))
}
@objc func createFolder() {
print("createFolder")
}
@objc func getInfo() {
print("getInfo")
}
// MARK: UIResponder
override var canBecomeFirstResponder: Bool { true }
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
touchesInSession = true
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
touchesInSession = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
touchesInSession = false
}
}
extension ViewController: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
guard !touchesInSession else { return nil }
let configuration = UIContextMenuConfiguration(identifier: "PointOnlyContextMenu" as NSCopying, previewProvider: { nil }, actionProvider: { suggestedActions in
let newFolder = UIAction(title: "New Folder", image: UIImage(systemName: "folder.badge.plus")) { [weak self] _ in
self?.createFolder()
}
let info = UIAction(title: "Get Info", image: UIImage(systemName: "info.circle")) { [weak self] _ in
self?.getInfo()
}
return UIMenu(title: "", children: [newFolder, info])
})
return configuration
}
}