iOS 14 setup the UIBarButttonItem menu before it is opened

Viewed 681

As iOS14 offers an easy way to setup the menu on the UIBarButtonItem, I wonder do I miss that there is no easy way to setup its items as they may depend on the current context.

I see that there is UIDeferredMenuElement, but this doesn't seem to be a good use case for it?

Also I see that there is primaryAction, but there doesn't seem to be a trivial way to call the menu from there.

And the third option seems to be reshuffling the menu on each change that may affect it, but that doesn't seem like a great idea.

I assume that there is something I miss out.

2 Answers

Basically for a simple bar button item there's no such provision. A UIControl like a UIButton has an event .menuActionTriggered, and it is perfectly reasonable, at that moment, to construct and assign the menu to the control, thus implementing a dynamic menu that is constructed just in the instant before the menu appears.

But you can't do that with a bar button item — unless it is a custom view bar button item with a UIButton in it.

What you'd have to do otherwise is watch the surrounding conditions via some other mechanism and just change the bar button item's menu each time the conditions change. It sounds like a pain in the butt but it's probably your only option. (I take it that that's exactly what you mean when you say "And the third option seems to be reshuffling the menu on each change that may affect it, but that doesn't seem like a great idea.")

I think what you're asking for is reasonable and it seems sort of silly that there's no provision for it; if you have a good use case, I'd recommend filing an enhancement request with Apple.

Here is the Swift 5 version of implementation, inspired by @matt 's answer.

private func setButton() {
    button.menu = UIMenu() // Here must be a placeholder menu. As the menu won't show if button.menu is nil.
    button.showsMenuAsPrimaryAction = true
    button.addTarget(self, action: #selector(createMenu(_:)), for: .menuActionTriggered)
}

@objc private func createMenu(_ button:UIButton) {
    ...
}

This is the only solution so far. As func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) doesn't called every time unless you hold for a while when a button is set to showsMenuAsPrimaryAction = true.

----edited----

If my answer was correct, so matt's. As my code was based on his idea. So I didn't think that matt was wrong at the first place. I just thought that the questioner had misunstanding with matt's answer.

Related