In WWDC 2019 and iOS 13 Apple introduced a new way to interact with your app: context menu. They look like this:

In this article I write about how to implement them.
Context menus is a logical continuation of “Peek and Pop” technology where a user may open some element’s preview using 3D touch. But there are several differences between them.
- Context menus are available on any device on iOS 13 or later. 3D touch support is not required. Thus, you can implement them on iPads.
- Action buttons appear immediately and don’t require swipe gesture.
In order to open context menu, user should touch and keep a finger on some element or press this element hard (if a device supports 3D touch).
How to use context menus?
In Human Interface Guidelines Apple recommends following several rules while prototyping context menus.
Adopt context menus consistently
It won’t be good if you add context menu for some elements somewhere and – don’t for other similar elements elsewhere. User may think that your app works incorrectly.
Include only necessary buttons to a context menu
Context menu is a great place for the most frequently used commands. “Most frequently used” is a key phrase. Don’t add everything into a menu.
Use submenus
Use submenus, so it would be easier for a user to find necessary command. Apply simple and clear names to commands.
Don’t use more than one level of submenus
Despite the fact that the submenus can make navigation easier, they can easily complicate it. Apple doesn’t recommend to use more than one level of submenus.
Put the most frequently used commands to the top of context menu
People mostly focus on the top of a menu, so it is wise to keep the most important commands at the top.
Group menu items
Use grouping to combine similar actions.
Don’t use context and edit menus for one UI element at the same time
There might be a conflict between them, because both of them are being triggered with a long tap.

Don’t add “Open” button to the menu
Users can open the element with a single tap. “Open” button will be redundant in this case.
Implementation of a simple context menu for UIView
Now let’s move to programming. Context menus are available for iOS 13+. You will also need Xcode 11.
You may download a full source code here.
Let’s add a context menu for UIImageView
, like in animation shown at the top of this article.
You should put UIImageView
object to the controller’s view and write a few lines of code in viewDidLoad
method:
class SingleViewController: UIViewController { @IBOutlet var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() imageView.isUserInteractionEnabled = true let interaction = UIContextMenuInteraction(delegate: self) imageView.addInteraction(interaction) } }
Firstly, we create an object of UIContextMenuInteraction
class. We provide a context menu’s delegate while initializing. We’ll look into it later. Using addInteraction
method we add our menu to the image.
It’s left to implement UIContextMenuInteractionDelegate
protocol now. There is only one required method, where we build our context menu:
extension SingleViewController: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { actions -> UIMenu<UIAction>? in let save = UIAction(__title: "My Button", image: nil, options: []) { action in // Put button handler here } return configuration } }
If we returned nil
, context menu wouldn’t be created. We create an object of UIContextMenuConfiguration
class inside this method. We pass the following parameters during initialization:
identifier
– context menu’s identifier.previewProvider
– custom controller that may be optionally shown instead of selected element. We’ll look into it later.- we pass elements of a menu into
actionProvider
.
It’s very easy to create a menu’s items: you should provide a name, optional icon and a handler. That’s all!
Adding a submenu
Let’s complicate a task. I want to add a context menu with 2 items: “Save” and “Edit…”. If user taps “Edit…” button, a submenu with “Rotate” and “Delete” items should be shown. The result looks like this:

To implement this we should rewrite UIContextMenuInteractionDelegate
in this way:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { actions -> UIMenu<UIAction>? in // Creating Save button let save = UIAction(__title: "Save", image: UIImage(systemName: "tray.and.arrow.down.fill"), options: []) { action in // Just showing some alert self.showAlert(title: action.title) } // Creating Rotate button let rotate = UIAction(__title: "Rotate", image: UIImage(systemName: "arrow.counterclockwise"), options: []) { action in self.showAlert(title: action.title) } // Creating Delete button let delete = UIAction(__title: "Delete", image: UIImage(systemName: "trash.fill"), options: .destructive) { action in self.showAlert(title: action.title) } // Creating Edit, which will open Submenu let edit = UIMenu<UIAction>.create(title: "Edit...", children: [rotate, delete]) // Creating main context menu return UIMenu<UIAction>.create(title: "Menu", children: [save, edit]) } return configuration }
Here we create “Save”, “Rotate” and “Delete” buttons, add the last two buttons into “Edit…” submenu and put everything to the main context menu.
Creating a context menu for UICollectionView
Let’s add a context menu into UICollectionView
. A user will see a menu with “Archive” button, like this:

UICollectionView
To implement this you should implement optional method func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
of UICollectionViewDelegate
protocol. Like this:
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { actions -> UIMenu<UIAction>? in let action = UIAction(__title: "Archive", image: UIImage(systemName: "archivebox.fill"), options: .destructive) { action in // Put button handler here } return UIMenu<UIAction>.create(title: "Menu", children: [action]) } return configuration }
Here we create an action and a menu. Now a user will see a context menu after a long tap.
Adding a menu to UITableView
It’s almost the same as UICollectionView.
You should implement contextMenuConfigurationForRowAt
method of UITableViewDelegate
protocol:
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { actions -> UIMenu<UIAction>? in let action = UIAction(__title: "Custom action", image: nil, options: []) { action in // Put button handler here } return UIMenu<UIAction>.create(title: "Menu", children: [action]) } return configuration }
But what if we want to show custom preview screen, like this:

UITableView
To do this, we should pass necessary UIViewController
into previewProvider
while creating UIContextMenuConfiguration
:
class PreviewViewController: UIViewController { static func controller() -> PreviewViewController { let storyboard = UIStoryboard(name: "Main", bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: "PreviewViewController") as! PreviewViewController return controller } } extension TableViewController: UITableViewDelegate { override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: { () -> UIViewController? in // Return Preview View Controller here return PreviewViewController.controller() }) { _ -> UIMenu<UIAction>? in let action = UIAction(__title: "Custom action", image: nil, options: []) { action in // Put button handler here } return UIMenu<UIAction>.create(title: "Menu", children: [action]) } return configuration } }
In this example PreviewViewController
is being initialized from a storyboard.
Let’s implement a tap onto this ViewController
. We should implement willCommitMenuWithAnimator
method of UITableViewDelegate
protocol. We will put the handler inside animator.addCompletion
:
override func tableView(_ tableView: UITableView, willCommitMenuWithAnimator animator: UIContextMenuInteractionCommitAnimating) { animator.addCompletion { // Put handler here } }
Conclusion
Context menu is a new powerful way to interact with your app. And, as you can see, it doesn’t take much time to implement it. But don’t forget that their implementation may be changed while iOS 13 is not being released.