How to show UIMenu from UIButton or UIBarButtonItem

Since iOS 14 we have a new and modern way of replacing UIAlertController with UIMenu.

UIMenu is great new way to provide user with on-demand inline options. Since iOS 13 we could show these menus from TableView and CollectionView cells by implementing one delegate method.

With iOS 14 these menus are getting ever more useful with the option of showing them from UIBarButtons and UIButtons. The UIBarButton use-case is a bit easier, so start with that.

Let's start with example menu definition:

var menuItems: [UIAction] {
    return [
        UIAction(title: "Standard item", image: UIImage(systemName: "sun.max"), handler: { (_) in   
        }),
        UIAction(title: "Disabled item", image: UIImage(systemName: "moon"), attributes: .disabled, handler: { (_) in 
        }),
        UIAction(title: "Delete..", image: UIImage(systemName: "trash"), attributes: .destructive, handler: { (_) in  
        })
    ]
}

var demoMenu: UIMenu {
    return UIMenu(title: "My menu", image: nil, identifier: nil, options: [], children: menuItems)
}

Because this is an example, I left the handlers empty. Next we can move to the UIBarButtonItem definition:

navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Menu", image: nil, primaryAction: nil, menu: demoMenu)

Since we haven't specified the primaryAction, the menu will appear immediately after tap.

UIMenu from UIBarButtonItem

UIButton + UIMenu

With buttons it is a little bit more complicated. If you want your UIMenu to appear immediately, you need to set the showsMenuAsPrimaryAction to true, otherwise the menu will appear after long press.

@IBOutlet var showMenuButton: UIButton!

func configureButtonMenu() {
    showMenuButton.menu = demoMenu
    showMenuButton.showsMenuAsPrimaryAction = true
}

With this setup code, after you tap the button, the menu will be automatically presented. Note that there is currently no init variant for UIButton that let's you specify pass the menu as a parameter.

UIMenu from UIButton

I found that this works very well as a replacement where you would traditionally use UIAlertController in the .actionSheet configuration. You don’t have to worry about which UIViewController is going to present the menu and there is no need to set the popover anchor when on iPad. That is a big win.

Uses: Xcode 12 & Swift 5.3


Follow me on Twitter for latest updates and news