New UIMenu and UIActions

At WWDC 2020 Apple announced a whole new set of interactions with the existing UIMenu and UIAction APIs. The small menus that were previously triggered by haptic/3D Touch can now be shown without them. Here’s how

This tutorial requires that you have Xcode 12 installed

Let’s make a simple UIKit app. I’ll be using storyboards in this tutorial as it’s simpler for what we’re going to do here. Make sure to embed your ViewController in a NavigationController so that we can access the navigationItems property on the Navigation Bar. That’s all we’re going to use the storyboard for

Let’s code

We’ll be creating a BarButton in this tutorial that will do an action when tapped and will show a menu on a long tap. Later in this tutorial, we’ll see how we can immediately present the menu without having to have the user long press on the BarButton Item. Exciting!

For the initial action, we’ll simply create a UIAction. A UIAction, just like a UIMenu, has most of its constructor parameters as optional so you can customize the things that you require and nothing else. For now, we’ll make a simple UIAction that has a title that can be discoverable on Voice Control and the closure for acting on a tap.

let saveAction = UIAction(title: "") { action in
    //Enter your Button Action Code here 
}

Now, let us create a menu. Menus are a quick and simple way to present users with multiple options/choices that they can act on. A menu for this part of the tutorial can be activated via long pressing on the bar button item. We’ll call this menu Save since it’ll contain some actions related to saving items.
The basic initializer for an UIMenu consists of a Title property and Children which is an array of UIActions. The title of the menu will be displayed at the top in the Menu Box and the children UIActions below it.
Each UIAction can be constructed in the way we did the saveAction.

let saveMenu = UIMenu(title: "", children: [
    UIAction(title: "Copy", image: UIImage(systemName: "doc.on.doc")) { action in 
            //Copy Menu Child Selected
        },
     UIAction(title: "Rename", image: UIImage(systemName: "pencil")) { action in 
            //Rename Menu Child Selected
        },
     UIAction(title: "Duplicate", image: UIImage(systemName: "plus.square.on.square")) { action in 
            //Duplicate Menu Child Selected
        },
     UIAction(title: "Move", image: UIImage(systemName: "folder")) { action in 
            //Move Menu Child Selected
        },
      ])

Each of the children has their own action closure that you can use to do whatever you want when the user selects that child action. All we have to do now is to set the actions and menu to a UIBarButton. For that, let’s create a UIBarButtonItem for saving.

let addButton = UIBarButtonItem(image: UIImage(systemName: "square.and.arrow.down"), primaryAction: saveAction, menu: saveMenu)

I’m using the systemImage from the SFSymbols library. Let’s understand the other two parameters a bit closely-

  • primaryAction: The action the button is supposed to do when the user taps on the button. It’s kind of like adding a target to the barButtonItem.

  • menu: The menu to be displayed when a long press/ Haptic Touch is performed on the barButtonItem.
    If you want to show the menu without any delay, and you don’t want to have a primary action for your button then you can omit the primaryAction parameter. This will cause the primary action to be displaying the saveMenu. Simple

All we have left to do is add this barButtonItem to the navigation bar.

navigationItem.rightBarButtonItem = addButton

And you’re done! Run this app and interact with the app and change the parameters to really experiment with the interaction.

If you liked this article you can

If you have any questions please feel free to ping me on Twitter @SwapnanilDhol