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 UIDeferredMenuElementthat lets us create dynamic menu items.


Adding UIMenu on UIButton

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.

An image where the button press shows menu on long press because showAsPrimaryAction is set to false.

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)

Adding UIMenu On UIBarButtonItem

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.

Image for post


iOS 14 UIDeferredMenuElement

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.

Image for post

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

What’s New in iOS 14's UIMenu and ContextMenu
97.10 GEEK