iOS UIMenuController UIMenuItem

Filed Under: iOS

In this tutorial, we’ll implement the UIMenuController element in our iOS Application.

UIMenuController

UIMenuController class is used to display an editing menu over the UI element. The editing menu is a rectangular box with different actions. By default, there are some inbuilt actions such as cut, copy, paste, lookup, replace, define etc.

Very commonly, this is also known as the iOS Tooltip Menu since it has handy commands for quick actions.

UIMenuItems are added to this UIMenuController.

Knowing how this works is easier to understand with an example.

Let’s get started by a very small basic project.

Code

Add a UITextField in your Main.storyboard in XCode as shown below:

ios uimenucontroller storyboard

Following is the code for ViewController.swift:


class ViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet weak var myTextField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        myTextField.delegate = self
        
        let menuController: UIMenuController = UIMenuController.shared
        
        menuController.setMenuVisible(true, animated: true)
        
     
        menuController.arrowDirection = UIMenuController.ArrowDirection.default
        
        menuController.setTargetRect(CGRect.zero, in: view)
        
        let menuItem1: UIMenuItem = UIMenuItem(title: "Menu 1", action: #selector(onMenu1(sender:)))
        let menuItem2: UIMenuItem = UIMenuItem(title: "Menu 2", action: #selector(onMenu2(sender:)))
        let menuItem3: UIMenuItem = UIMenuItem(title: "Menu 3", action: #selector(onMenu3(sender:)))
        
        // Store MenuItem in array.
        let myMenuItems: [UIMenuItem] = [menuItem1, menuItem2, menuItem3]
        
        // Added MenuItem to MenuController.
        menuController.menuItems = myMenuItems
    }

    override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool {
            return true
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    @objc internal func onMenu1(sender: UIMenuItem) {
        print("onMenu1")
    }
    
    @objc internal func onMenu2(sender: UIMenuItem) {
        print("onMenu2")
    }
    
    @objc internal func onMenu3(sender: UIMenuItem) {
        print("onMenu3")
    }
    
}

Let’s analyze what is happening in the above code.

  • UIMenuController is a singleton instance. Hence initialised using shared.
  • Setting the property arrowDirection to default. The default direction is top or down depending on the location in the screen.
  • setTargetRect is used to set the position of the MenuController. In the above code, we’ve set the CGRect as zero means it will show the MenuController just above/below the UI element.
But how does the MenuController know where to be displayed and for whom?

Answer: UIResponder.

UIResponder is responsible for keeping a responder chain hierarchy. Similar to the view hierarchy.
The object which is the first responder gets all the new events. Generally, a UITextField is the first responder.

To set another UI element as the first responder we can call becomeFirstResponder

To create an instance of UIMenuItem we can define it as:

UIMenuItem(title: , action: ) – the title is the string and the action is the selector function that gets triggered when that UIMenuItem is clicked.

Then we set the array of MenuItem on the MenuController, by setting it on the menuController.menuItems.

The canPerformAction(_:withSender:) is a part of UIResponder. Returning true means all actions would be shown. Let’s see what are the all actions.

Let’s run the above iOS Application on the simulator and see the output.

ios uimenucontroller output 1

WHOOPS! It shows so many actions. Why so?

By default, we are returning true in the canPerformAction. This returns all the actions.

In order to only show the actions we want, we can change the canPerformAction function to:


override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool {
        if [#selector(onMenu1(sender:)), #selector(onMenu2(sender:)), #selector(onMenu3(sender:))].contains(action) {
            return true
        }
        else{
            return false
        }
    }

This way we are checking if the action belongs to either of the above selectors. Otherwise, we return false. To include any of the default actions we can simply just include their built-in selectors. Some of the built-in selectors look like:

  • #selector(cut(_:))
  • #selector(copy(_:))
  • #selector(paste(_:))

The built-in implementation of the above methods is available in the UIResponderStandardEditActions protocol. We can override them as well.

We’ve added the cut selector in the above if condition. This is how the final output looks:

ios uimenucontroller output

In the next part of this tutorial, we’ll implement UIMenuController on multiple elements and also implement the custom actions along with changing the MenuItems dynamically.

You can download the source code for this project from the link below and try adding your actions in the selector functions.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages