Как программно вызвать контекстное меню UIContextMenuInteraction?

Я установил UIButton как rightBarButtonItem в UIViewController внутри UINavigationController и связал с ним контекстное меню iOS13.

Как и ожидалось, долгое нажатие на кнопку показывает контекстное меню.

Есть ли способ показать контекстное меню, нажав кнопку (например, добавив цель для события .touchUpInside)?

Кнопка / barButtonItem настраивается следующим образом:

let button = UIButton(type: .system)
button.setImage(UIImage(systemName: "plus"), for: .normal)

let barButton = UIBarButtonItem(customView: button)
self.navigationItem.rightBarButtonItem = barButton

let interaction = UIContextMenuInteraction(delegate: self)
button.addInteraction(interaction)

Контекстное меню определяется следующим образом:

extension ViewController: UIContextMenuInteractionDelegate {
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
            let importAction = UIAction(title: "Import", image: UIImage(systemName: "folder")) { action in }
            let createAction = UIAction(title: "Create", image: UIImage(systemName: "square.and.pencil")) { action in }
            return UIMenu(title: "", children: [importAction, createAction])
        }
    }
}

person mmklug    schedule 01.09.2019    source источник
comment
тоже нужно было что-то подобное. Не повезло!   -  person Ivan Cantarino    schedule 05.05.2020
comment
@IvanCantarino удачи? Я знаю, что UIKit управляет всем взаимодействием с контекстным меню, но есть и другие примеры элементов, управляемых UIKit, которые можно запускать программно. Вы, вероятно, можете (программно) смоделировать долгое нажатие, но я ищу более элегантное решение (например, просто вызовите место на экране, чтобы его представить).   -  person Justin Bush    schedule 05.05.2020
comment
Ну, поскольку я искал что-то подобное для приложения Mac Catalyst, мне удалось использовать UIMenuController с библиотекой PSMenuItem, хотя имитация UIContextMenu была бы предпочтительнее tbh.   -  person Ivan Cantarino    schedule 05.05.2020
comment
Я пришел обновить свой ответ, упомянув о новом дополнении в iOS 14, и обнаружил, что вы уже обновили свой вопрос :). Я бы просто предложил добавить это в качестве нового ответа на ваш вопрос, и это совершенно нормально.   -  person Hejazi    schedule 10.07.2020
comment
Спасибо, @Hejazi, я добавил новый ответ, как было предложено.   -  person mmklug    schedule 13.07.2020


Ответы (2)


Контекстное меню, по замыслу, автоматически отображается системой, когда происходит соответствующий жест (силовое касание или долгое нажатие). Вы не можете показать это вручную.

Из документов:

Объект взаимодействия с контекстным меню отслеживает жесты Force Touch на устройствах, поддерживающих 3D Touch, и жесты длительного нажатия на устройствах, которые его не поддерживают.

UIKit управляет всеми взаимодействиями, связанными с меню, и сообщает о выбранном действии, если таковое имеется, обратно в ваше приложение.

ОБНОВЛЕНИЕ:

Хотя по-прежнему невозможно вручную отобразить контекстное меню в iOS 14, теперь можно отображать UIMenu, который мы создали для контекстного меню, как раскрывающееся меню. Посмотрите ответ @ Lobo, чтобы узнать, как это сделать на iOS 14.

person Hejazi    schedule 03.09.2019

iOS 14

iOS 14 (первая бета) теперь поддерживает желаемую функциональность. При нажатии следующего кода на UIBarButtonItem меню немедленно отображается (также избегая размытого фона, который возникает в результате вызова UIContextMenuInteraction):

override func viewDidLoad() {
    super.viewDidLoad()
    
    let importAction = UIAction(title: "Import", image: UIImage(systemName: "folder")) { action in }
    let createAction = UIAction(title: "Create", image: UIImage(systemName: "square.and.pencil")) { action in }
    
    let menuBarButton = UIBarButtonItem(
        title: "Add",
        image: UIImage(systemName:"plus"),
        primaryAction: nil,
        menu: UIMenu(title: "", children: [importAction, createAction])
    )
    
    self.navigationItem.rightBarButtonItem = menuBarButton
}

Функциональность достигается за счет отсутствия primaryAction.

Вы можете добиться того же эффекта, используя UIButton. В этом случае вам нужно будет установить

button.showsMenuAsPrimaryAction = true

Полный код UIButton может выглядеть так:

override func viewDidLoad() {
    super.viewDidLoad()
   
    let button = UIButton(type: .system)
    button.setImage(UIImage(systemName: "plus"), for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(button)
    
    NSLayoutConstraint.activate([
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
    ])
    
    let importAction = UIAction(title: "Import", image: UIImage(systemName: "folder")) { action in }
    let createAction = UIAction(title: "Create", image: UIImage(systemName: "square.and.pencil")) { action in }
    
    let items = [importAction, createAction]
    
    button.menu = UIMenu(title: "Add", children: items)
    button.showsMenuAsPrimaryAction = true
}

person mmklug    schedule 13.07.2020
comment
Привет, есть ли способ программно выбрать кнопку, а затем показать меню? На данный момент. Я могу вызвать UIMenu только вручную, нажав кнопку. Я хочу отображать меню напрямую, когда пользователь входит в контроллер представления. Заранее спасибо... - person Foster; 20.07.2020
comment
Я пытаюсь сделать то же самое, но при срабатывании событий меню вообще не отображается. Вы что-то понимаете? - person guiltance; 24.08.2020
comment
Проголосовали за button.showsMenuAsPrimaryAction - person Ayan Sengupta; 01.07.2021