Basic Context Menus
This episode is part of a series: Working with Context Menus.
1. Basic Context Menus 12 min |
2. Nested / Inline Menus 5 min |
3. Table View Menus 8 min |
4. Context Menu Previews 8 min |
Want more? Subscribers can view all 485 episodes. New episodes are released regularly.
Context menus are a great affordance for performing related actions to a UI element. Users can tap and hold to view the context menu, and the gesture is consistent across the OS so users will likely already be familiar with it.
Let's see how we can add a basic context menu to this avatar view to set a custom avatar.
First we need to add a context menu interaction:
private func setupContextMenu() {
let interaction = UIContextMenuInteraction(delegate: self)
avatarImageView.isUserInteractionEnabled = true
avatarImageView.addInteraction(interaction)
}
Note that we have to enabled user interaction on our image view, otherwise it won't receive any touch events. This is typically only true for UIImageView
, but if you find a view is not responding to the tap-and-hold gesture, make sure that this property is set to true.
Next we need to conform our view controller to the UIContextMenuInteractionDelegate
. This allows us to define the menu on-demand, which is useful if the data we need to display is dynamic.
extension ViewController: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
}
}
The only required method is one that asks for a UIContextMenuConfiguration
. Since this method could be used to serve up many different context menus in our view controller, we'll first check which view this was for.
guard interaction.view === avatarImageView else { return nil }
Next we'll create a UIContextMenuConfiguration
, which accepts a couple of params (that will be nil for now) as well as a block that is used to build the menu.
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (suggestedActions) -> UIMenu? in
let chooseAvatar = UIAction(title: "Choose Avatar", image: UIImage(systemName: "pencil"), identifier:
nil) { _ in
self.selectAvatar()
}
return UIMenu(title: "Avatar", image: nil, identifier: nil, options: [], children: [
chooseAvatar
])
Next we can utilize UIImagePickerController
to select a photo from the library and use that as an avatar.
This works, but it would be nice to be able to remove that avatar as well. Let's add another context menu action to remove it.
let removeAvatar = UIAction(title: "Remove", image: UIImage(systemName: "trash"), identifier: nil,
attributes: [.destructive]) { _ in
self.setDefaultAvatar()
}
// don't forget to add it as a child
return UIMenu(title: "Avatar", image: nil, identifier: nil, options: [], children: [
chooseAvatar, removeAvatar
])
Note that we used the .destructive attribute, so our menu item becomes red.
And tapping the Remove button will remove our custom avatar. Awesome!
Ok one last thing that is bugging me is the white rectangle around our avatar view. Let's see how we can remove that.
Back in the UIContextMenuInteractionDelegate
we can implement another method, one that asks for a preview for highlighting:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction,
previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) ->
UITargetedPreview? {
let params = UIPreviewParameters()
params.backgroundColor = .clear
let preview = UITargetedPreview(view: avatarImageView, parameters: params)
return preview
}
Here we can specify custom parameters like shadow path, visible shape, and background color, as well as whatever view we want to show in the highlighted state. Setting the background to .clear and using our existing avatarImageView works great, and now we don't have that white rectangle.
So that's the basics of how context menus work. In the next few episodes we will explore some more advanced topics and see what else we can do with these context menus. Stay tuned!