0

I want to present a menu from a UIBarButtonItem, but only if a runtime check succeeds when the button is tapped, otherwise show an alert.

Quick background. I had some older code (pre-UIMenu days) that handled the UIBarButtonItem with a target/action that would perform the check and then either show an alert or present an action sheet.

I'm trying to update that code to use a UIMenu instead of an action sheet (UIAlertController). I know how to create the UIBarButtonItem with a UIMenu. That's easy to implement.

What I can't find in any APIs or in any searching here on SO, is how to manually display a UIMenu.

Here's a rough example of my code that directly shows a menu from the UIBarButtonItem:

btnAdd = UIBarButtonItem(systemItem: .add, menu: UIMenu(children: [
    // An array of UIAction instances for each menu item
]))

That code works just fine but I need to change it so the menu only appears under the right condition. I'm thinking of something like the following but I don't know how to write the line of code that manually displays a UIMenu.

btnAdd = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addAction))

...

@objc func addAction(_ sender: UIBarButtonItem) {
    if someRuntimeCondition == true {
        let menu = UIMenu(children: [
            // An array of UIAction instances for each menu item
        ])

        ??? // How to display menu from sender?
    } else {
        // Create and display an alert
    }
}

I feel like I'm missing something simple and obvious but I just don't see it.

I've reviewed the documentation for UIMenu, UIBarButtonItem, UIContextMenuInteraction, and UIMenuController (deprecated). None of these seem to provide a way to manually display a menu from a UIBarButtonItem. I've also looked at a couple of Apple's sample apps.

Any solution needs to work with iOS 15.0+.

HangarRash
  • 7,314
  • 5
  • 5
  • 32
  • Even though I found a working solution, if anyone does know how to manually display a `UIMenu` from a `UIBarButtonItem`, please post an answer. I'm still curious if that is possible. – HangarRash Jun 24 '23 at 22:05

1 Answers1

0

I was seconds away from clicking Submit on my question when I found a tricky solution using UIDeferredMenuElement.uncached. This doesn't answer the question of how to manually display a UIMenu from a UIBarButtonItem but this does achieve my ultimate goal of conditionally showing either an alert or a menu when the UIBarButtonItem is tapped.

btnAdd = UIBarButtonItem(systemItem: .add, menu: UIMenu(children: [
    UIDeferredMenuElement.uncached({ [weak self] completion in
        if someRuntimeCondition == true {
            completion([]) // send back an empty list of menu items
            // Show UIAlertController
        } else {
            let children = [
                // An array of UIAction instances for each menu item
            ]
            completion(children) // send back the proper menu items
        }
    })
]))
HangarRash
  • 7,314
  • 5
  • 5
  • 32