I'm attempting to create a custom menu for each view in my app, however it appears buildMenu is not being called in View Controllers. Here's an example:
In my AppDelegate, this code is used, which works 100% as expected.
override func buildMenu(with builder: UIMenuBuilder) {
print("Updating menu from AppDelegate")
super.buildMenu(with: builder)
let command = UIKeyCommand(
input: "W",
modifierFlags: [.command],
action: #selector(self.helloWorld(_:))
)
command.title = "Hello"
builder.insertChild(UIMenu(
__title: "World",
image: nil,
identifier: UIMenu.Identifier(rawValue: "com.hw.hello"),
options: [],
children: [command]
), atEndOfMenu: .file)
}
@objc private func helloWorld(_ sender: AppDelegate) {
print("Hello world")
}
However I need to change the options available in the menu depending on where the user is in the app, so I tried doing this in a UIViewController:
override func viewDidAppear(_ animated:Bool){
// Tried all of these to see if any work
UIMenuSystem.main.setNeedsRebuild()
UIMenuSystem.context.setNeedsRebuild()
UIMenuSystem.main.setNeedsRevalidate()
UIMenuSystem.context.setNeedsRevalidate()
}
and again..
// This is never called
override func buildMenu(with builder: UIMenuBuilder) {
print("Updating menu in View Controller")
}
but the buildMenu in the UIViewController is never called :(
Any ideas if this is intended behavior or if there are any workarounds?
For main menus, the system only consults UIApplication
and UIApplicationDelegate
, since main menus can exist without any window and hence without any UIViewController
hierarchy. That's why your override on UIViewController
doesn't get called for main menus.
For context menus, the system does consult the full responder chain starting at the view.
If you need to update main menu commands depending on their context:
buildMenu(with:)
in UIApplicationDelegate
, arrange for delegate to figure out when and what changed and call UIMenuSystem.main.setNeedsRebuild()
when it does change, orbuildMyMenu(with:)
in your UIViewController
subclasses, and arrange for buildMenu(with:)
in UIApplicationDelegate
to call it, orbuildMenu
, and rely on your overrides of canPerformAction(_:withSender:)
and validate(_:)
to enable or disable or even hide particular commands e.g. by updating the attributes
property in your validate(_:)
override.This is the intended behavior. Quoting from docs:
Because menus can exist with no window or view hierarchy, the system only consults
UIApplication
andUIApplicationDelegate
to build the app’s menu bar.
The same docs page explains how you can adjust the menu commands from view controllers, and there is a great sample project too, so make sure to check it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With