Simple way to show the 'Copy' popup on UITableViewCells like the address book App

There is now official interface for displaying UITableView cell menus in iOS 5. Example (from the table delegate):

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    return (action == @selector(copy:));
}

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    if (action == @selector(copy:)){
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        [[UIPasteboard generalPasteboard] setString:cell.textLabel.text];
    }
}

I tried modifying the UIMenuController's shared controller to add my own menu item, and I was able to add it and get the canPerformAction message for it, but returning YES didn't help; I wasn't able to make my custom menu item appear. From my experiments, it looks like only Copy, Cut, and Paste are supported. [EDIT Since this was posted, I've learned how to add custom menu items.]

Note that this works only if all three delegate methods are implemented.


The method before iOS 5 is to get the UIMenuController's shared instance, set the target rect and view and call -setMenuVisible:animated:. Remeber to implement -canPerformAction:withSender: in your responder.


The method after iOS 5 (previously available as undocumented feature) is to implement these 3 methods in your data source (see https://developer.apple.com/reference/uikit/uitableviewdelegate#1653389).

-(void)tableView:(UITableView*)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender;
-(BOOL)tableView:(UITableView*)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender;
-(BOOL)tableView:(UITableView*)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath*)indexPath; 

Here is the Swift syntax for copying detailTextLabel.

func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
    return (tableView.cellForRow(at: indexPath)?.detailTextLabel?.text) != nil
}

func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return action == #selector(copy(_:))
}

func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
    if action == #selector(copy(_:)) {
        let cell = tableView.cellForRow(at: indexPath)
        let pasteboard = UIPasteboard.general
        pasteboard.string = cell?.detailTextLabel?.text
    }
}