When Apple added cut/copy/paste support in iOS 3.0, it also provided developers the ability to customize the popover menu via UIMenuController. Unfortunately, that initial implementation required non-trivial preparations by developers just to change the menu:

  1. The view attached to the menu must return YES for canBecomeFirstResponder, which meant it had to be subclassed. For example, two of the most popular display elements, UITableViewCell and UILabel, return NO by default.
  2. UILongPressGestureRecognizer was not available until iOS 3.2, which means that the long press to initiate the menu display had to be implemented via touchesBegan:withEvent:, touchesMoved:withEvent:, and touchesEnded:withEvent:. Every custom long press recognizer might use a different delay constant, which could easily confuse users who are used to another app's implementation.

Later iOS releases have streamlined this situation into two basic approaches: one for table cells and one for custom menu options.

Special Case: UITableViewCell on iOS 5

If you only need to use the system-provided options of cut, copy, or paste for a UITableViewCell (by far the most common use), Apple has included a great shortcut in iOS 5.0. All it takes is for you to implement three new UITableViewDelegate methods to tell the OS which options you want to use and what selectors to call when each is chosen. Here is an example that shows the copy button:

<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">BOOL</span><span class="br0">)</span>tableView<span class="sy0">:</span><span class="br0">(</span>UITableView <span class="sy0">*</span><span class="br0">)</span>tableView shouldShowMenuForRowAtIndexPath<span class="sy0">:</span><span class="br0">(</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/"><span class="kw5">NSIndexPath</span></a> <span class="sy0">*</span><span class="br0">)</span>indexPath <span class="br0">{</span>
 <span class="kw1">return</span> <span class="kw2">YES</span>;
<span class="br0">}</span>
 
<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">BOOL</span><span class="br0">)</span>tableView<span class="sy0">:</span><span class="br0">(</span>UITableView <span class="sy0">*</span><span class="br0">)</span>tableView canPerformAction<span class="sy0">:</span><span class="br0">(</span><span class="kw4">SEL</span><span class="br0">)</span>action forRowAtIndexPath<span class="sy0">:</span><span class="br0">(</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/"><span class="kw5">NSIndexPath</span></a> <span class="sy0">*</span><span class="br0">)</span>indexPath withSender<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 <span class="kw1">if</span> <span class="br0">(</span>action <span class="sy0">==</span> <span class="kw1">@selector</span><span class="br0">(</span>copy<span class="sy0">:</span><span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
 <span class="kw1">return</span> <span class="kw2">YES</span>; 
 <span class="br0">}</span>
 
 <span class="kw1">return</span> <span class="kw2">NO</span>; 
<span class="br0">}</span>
 
<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>tableView<span class="sy0">:</span><span class="br0">(</span>UITableView <span class="sy0">*</span><span class="br0">)</span>tableView performAction<span class="sy0">:</span><span class="br0">(</span><span class="kw4">SEL</span><span class="br0">)</span>action forRowAtIndexPath<span class="sy0">:</span><span class="br0">(</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/"><span class="kw5">NSIndexPath</span></a> <span class="sy0">*</span><span class="br0">)</span>indexPath withSender<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 <span class="kw1">if</span> <span class="br0">(</span>action <span class="sy0">==</span> <span class="kw1">@selector</span><span class="br0">(</span>copy<span class="sy0">:</span><span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
 <span class="br0">[</span>UIPasteboard generalPasteboard<span class="br0">]</span>.<span class="kw4">string</span> <span class="sy0">=</span> <span class="br0">[</span>data objectAtIndex<span class="sy0">:</span>indexPath.row<span class="br0">]</span>;
 <span class="br0">}</span>
<span class="br0">}</span>

The menu calls tableView:canPerformAction:forRowAtIndexPath:withSender to determine if it should show each system menu option and calls tableView:performAction:forRowAtIndexPath:withSender: when the user selects an option. This functionality should cover the vast majority of use cases, but it only works with system-provided actions.

Custom Menu Items

The code is a little more involved if you want to use custom menu options, but offers a lot of flexibility. It's your responsibility to detect the long press and show the custom menu, and the easiest way to do this is using UILongPressGestureRecognizer on the table cell:

UILongPressGestureRecognizer <span class="sy0">*</span>recognizer <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UILongPressGestureRecognizer alloc<span class="br0">]</span> initWithTarget<span class="sy0">:</span>self action<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">(</span>longPress<span class="sy0">:</span><span class="br0">)</span><span class="br0">]</span>;
<span class="br0">[</span>cell addGestureRecognizer<span class="sy0">:</span>recognizer<span class="br0">]</span>;

For the menu to appear, the target view must be in the responder chain. Many UIKit views can't become a responder by default, so you may need to subclass them to return YES for canBecomeFirstResponder. In the example below, we use the custom class TSTableViewCell and then implement the long press selector on the view controller:

<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>longPress<span class="sy0">:</span><span class="br0">(</span>UILongPressGestureRecognizer <span class="sy0">*</span><span class="br0">)</span>recognizer <span class="br0">{</span> 
 <span class="kw1">if</span> <span class="br0">(</span>recognizer.state <span class="sy0">==</span> UIGestureRecognizerStateBegan<span class="br0">)</span> <span class="br0">{</span>
 TSTableViewCell <span class="sy0">*</span>cell <span class="sy0">=</span> <span class="br0">(</span>TSTableViewCell <span class="sy0">*</span><span class="br0">)</span>recognizer.view;
 <span class="br0">[</span>cell becomeFirstResponder<span class="br0">]</span>;
 
 UIMenuItem <span class="sy0">*</span>flag <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UIMenuItem alloc<span class="br0">]</span> initWithTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"Flag"</span> action<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">(</span>flag<span class="sy0">:</span><span class="br0">)</span><span class="br0">]</span>;
 UIMenuItem <span class="sy0">*</span>approve <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UIMenuItem alloc<span class="br0">]</span> initWithTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"Approve"</span> action<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">(</span>approve<span class="sy0">:</span><span class="br0">)</span><span class="br0">]</span>;
 UIMenuItem <span class="sy0">*</span>deny <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UIMenuItem alloc<span class="br0">]</span> initWithTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"Deny"</span> action<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">(</span>deny<span class="sy0">:</span><span class="br0">)</span><span class="br0">]</span>;
 
 UIMenuController <span class="sy0">*</span>menu <span class="sy0">=</span> <span class="br0">[</span>UIMenuController sharedMenuController<span class="br0">]</span>;
 <span class="br0">[</span>menu setMenuItems<span class="sy0">:</span><span class="br0">[</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/"><span class="kw5">NSArray</span></a> arrayWithObjects<span class="sy0">:</span>flag, approve, deny, <span class="kw2">nil</span><span class="br0">]</span><span class="br0">]</span>;
 <span class="br0">[</span>menu setTargetRect<span class="sy0">:</span>cell.frame inView<span class="sy0">:</span>cell.superview<span class="br0">]</span>;
 <span class="br0">[</span>menu setMenuVisible<span class="sy0">:</span><span class="kw2">YES</span> animated<span class="sy0">:</span><span class="kw2">YES</span><span class="br0">]</span>;
 <span class="br0">}</span>
<span class="br0">}</span>
 
<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>flag<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 NSLog<span class="br0">(</span><span class="co3">@</span><span class="st0">"Cell was flagged"</span><span class="br0">)</span>;
<span class="br0">}</span>
 
<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>approve<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 NSLog<span class="br0">(</span><span class="co3">@</span><span class="st0">"Cell was approved"</span><span class="br0">)</span>;
<span class="br0">}</span>
 
<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>deny<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 NSLog<span class="br0">(</span><span class="co3">@</span><span class="st0">"Cell was denied"</span><span class="br0">)</span>;
<span class="br0">}</span>

There is only one small gotcha with UIMenuItem: if the specified action is not implemented by your view controller, that item will not appear in the menu.