UIMenu
was introduced in iOS 13 to allow easy creation of menus and submenus, which is really handy when building context menus.
iOS 14 brings more customization to UIMenu
by introducing out-of-the-box support for UIButton
and UIBarButtonItem
through the menu
property and initializers respectively.
In the following sections, we’ll look at how to set the menu on the above-mentioned UI controls. We’ll also look at the new UIDeferredMenuElement
that lets us create dynamic menu items.
By assigning the UIMenu
to the new menu
property on UIButton
, UIKit automatically takes care of displaying that menu on a long press.
let destruct = UIAction(title: "Destruct", attributes: .destructive) { _ in }
let items = UIMenu(title: "More", options: .displayInline, children: [
UIAction(title: "Item 1", image: UIImage(systemName: "mic"), handler: { _ in }),
UIAction(title: "Item 2", image: UIImage(systemName: "envelope"), handler: { _ in }),
UIAction(title: "Item 3", image: UIImage(systemName: "flame.fill"), handler: { _ in }),
UIAction(title: "Item 4", image: UIImage(systemName: "video"), state: .on, handler: { _ in })
])
button.menu = UIMenu(title: "", children: [items, destruct])
Notably, setting the displayInline
as the option
displays the submenu within the parent menu itself.
To show the menu immediately on button tap, we need to set the showsMenuAsPrimaryAction
property to true
:
button.showsMenuAsPrimaryAction = true
In order to determine if the menu is triggered and perhaps perform some other action, iOS 14 has introduced a new control event for UIButton
, namely, menuActionTriggered
. We can set it to fire the UIAction
handler in the following way:
button.addAction(UIAction(title: ""){ _ in print("Hello Menu")},for: .menuActionTriggered)
Besides providing the menu
property to create and display UI menus, the UIBarButtonItem
also introduces an initializer wherein you can pass the UIMenu
group and primary actions as optional arguments.
The following code shows how to construct UIBarButtonItems
with primaryAction
and menu
initialisers:
self.toolbarItems = [
UIBarButtonItem(image: UIImage(systemName: "square.and.arrow.up.fill"), primaryAction: action, menu: items),
.fixedSpace(width:20.0),
UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), menu: menu1),
.flexibleSpace(),
UIBarButtonItem(primaryAction: action)
]
The important difference between UIButton
and UIBarButtonItem
menu action is that, for the latter, there is no showAsPrimaryAction
property. So, if you want the UIBarButtonItem
to display the menu immediately on touchdown, don’t set a primaryAction
.
UIDeferredMenuElement
is a powerful new component introduced in iOS 14 that lets you add menu items in an asynchronous fashion.
In order to so, you provide a standard placeholder menu that gets replaced with the deferred menu elements once it’s loaded.
let deferredMenus = UIMenu(title: "", children: [
UIDeferredMenuElement({ completion in
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
let items = (1...2).map { UIAction(title: "Dynamic Menu Item \($0)") { action in } }
completion([UIMenu(title: "", options: .displayInline, children: items)])
}
})
])
button.menu = deferredMenus
Deferred menus provide a built-in caching mechanism that ensures that subsequent loads of the same instance are faster.
UIDeferredMenuElement
is also useful for generating deeply nested menus where the items won’t be displayed until they’re needed.
#design #software-development #ios #programming #swift