How to present UIMenu manually on a button tap?

2k Views Asked by At

I would like to present my UIMenu from UIButton when button is tapped. Because in the beginning I need to update children of UIMenu.

let menuButton: UIButton = {
    let button = UIButton()
    button.menu = UIMenu(title: "title")
    button.showsMenuAsPrimaryAction = true
    return button
}()

func setupView() {
    menuButton.rx.tap.bind {
        let action = UIAction(title: "title", image: nil, handler: { _ in })
        menuButton.menu.replacingChildren([action])
//            present menu, how?
    }.disposed(by: disposeBag)
}

Nothing happens here. My action for tap is registered with RxSwift. How can I do it to present the menu?

2

There are 2 best solutions below

0
Bartłomiej Semańczyk On BEST ANSWER

There is a need to add system .menuActionTriggered action:

let menuButton: UIButton = {
    let button = UIButton()
    button.menu = UIMenu(title: "")
    button.showsMenuAsPrimaryAction = true
    return button
}()

menuButton.addAction(UIAction(title: "") { _ in
    menuButton.menu = UIMenu(title: "newtitle", children: [])
    // this block is called BEFORE new menu appear on the screen, and anything may be modified before presentation
}, for: .menuActionTriggered)

replacingChildren([action]) return a copy of new UIMenu which need to be assigned to .menu property again.

0
Kumar Lav On

I hope my answer will work for you. This is pretty easy in iOS 14 and above.

By default, the nature of UIMenu to launch is on Long Press.

To make it a single click to open UIMenu iOS 14 provides a single line of code.

 @IBAction func open(_ sender: UIButton) {
        if #available(iOS 14.0, *) {
            sender.menu = self.getContextMenu(data: "Copy this data")
            sender.showsMenuAsPrimaryAction = true
        } else {
            // Fallback on earlier versions
        }
    }

button.showsMenuAsPrimaryAction = true

To find the complete code got to my Github