iPadOS: предотвращение срабатывания UIContextMenuInteraction, когда указатель не используется
UIMenu
против UIContextMenuInteraction
против UIPointerInteraction
Я пытаюсь настроить UIContextMenuInteraction
так же, как в приложении Файлы или Страницы:
- (Долгое) нажатие в любом месте пустого пространства показывает черный горизонтальный
UIMenu
- Дополнительный щелчок (вправо /Control) с указателем в любом месте пустого пространства показывает контекстное меню
См. Демонстрацию в прикрепленном GIF-изображении ниже.
Я могу настроить UIContextMenuInteraction
и в его UIContextMenuInteractionDelegate
вернуть UIContextMenuConfiguration
с предметами, которые я хочу показать.
То же самое для маленького черного UIMenu
, Я мог бы использовать UILongPressGestureRecognizer
и показать меню, используя UIMenuController.shared.showMenu
.
Однако я не могу предотвратить UIContextMenuInteraction
от запуска и отображения UITargetedPreview
при долгом нажатии на вид, и теперь кажется, что есть способ распознать разные UITouchType
s с информацией, предоставленной UIContextMenuInteractionDelegate
.
Так же не нашел как программно показать контекстное меню, без UIContextMenuInteraction
. Есть способ сделать это?
Вопрос
Как это реализовано в Files.app?
1 ответ
Невозможно программно вызвать контекстное меню, но с помощью некоторой простой бухгалтерии вы можете предотвратить его отображение, когда оно не требуется (например, когда на вашем респонденте активны касания).
Чтобы скрыть предварительный просмотр, просто верните nil из previewProvider
в инициализаторе UIContextMenuConfiguration.
Вот полная реализация с представлением контроллера представления в качестве цели:
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
}
}