iOS: UIPasteboard, UIMenuController & UIMenuItem

There are times that you need to copy and paste items on your device, few words won’t be a problem but several paragraphs will. UIPasteboard is a built-in class that allows you to copy items to the pasteboard in iOS and paste it somewhere else. A user then selects an item (e.g., words, images, etc..) from any field where text and images are allowed to be copied and paste the contents currently in the pasteboard. There’s a high chance your app will have data or information that users will need to copy and paste to save it somewhere else such as the Notes app. Flight plans, PDF, images, or a full paragraph, you name it, users are forced to solve problems creatively for lack of a provided solution.

Class API

Let’s look at a simple implementation, and then dive into some specifics about the APIs.
[[UIPasteboard generalPasteboard] setString:self.text];
gerenalPasteboard returns the general pasteboard, which is used for general copy-paste operations. You may use the general pasteboard for copying and pasting text, images, URLs, colors, and other data within an app or between apps. The general pasteboard is persistent across device restarts and app uninstalls. setString: is a setter of the string property that sets the string value of the first pasteboard item. The value stored in this property is an NSString object. The associated array of representation types is UIPasteboardTypeListString, which includes type kUTTypeUTF8PlainText. Setting this property replaces all current items in the pasteboard with the new item. If the first item has no value of the indicated type, nil is returned. There are three ways to initialise an instance of UIPasteboard:
+ (UIPasteboard *)generalPasteboard;
+ (UIPasteboard *)pasteboardWithName:(NSString *)pasteboardName create:(BOOL)create;
+ (UIPasteboard *)pasteboardWithUniqueName;
The general pasteboard is persistent across device restarts and app uninstalls.
The first, generalPasteboard was explained above. The second method returns a pasteboard identified by name using the parameter pasteboardName, optionally creating it if it doesn’t exist. The third returns an app pasteboard identified by a unique system-generated name.

Coding in Style

There are different ways of implementing a copy and paste process, subclass or not, it will be your own decision what suits your app best. You can create a button with a method that will copy a certain text from a label or a textview:
- (void)copyButtonPressed:(id)sender
{
    UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard];
    generalPasteboard.string = self.label.text;
}
This is not applicable many objects in a view though, so it’s best to subclass certain objects.

Subclassing

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.
UILabel must be subclassed to implement canBecomeFirstResponder and canPerformAction:withSender:
Any time you copy or cut anything in iOS, that content gets posted to an instance of UIPasteboard. The pasteboard is a powerful and type agnostic way to move content from one application to another or between parts of a single application. The pasteboard is capable of storing nearly any type of data, not just text. This feature is present since iOS 3 but is rarely used by application developers even though it’s very easy to implement.
PasteboardLabel.{h,m}
@interface PasteboardLabel : UILabel

@end

@implementation PasteboardLabel

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    return (action == @selector(copy:));
}

#pragma mark - UIResponderStandardEditActions
- (void)copy:(id)sender
{
    UIPasteboard *generalPasteboard = [UIPasteboard generalPasteboard];
    generalPasteboard.string = self.text;
}

@end
ViewController.m
- (void)viewDidLoad
{
    PasteboardLabel *label = [[PasteboardLabel alloc] initWithRect:...];
    label.userInteractionEnabled = YES;
    [self.view addSubview:label];
    
    UIGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecognizer:)];
    [label addGestureRecognizer:gestureRecognizer];
}

#pragma mark - Gestures
- (void)longPressGestureRecognizer:(UIGestureRecognizer *)recognizer
{
    UIMenuController *menuController = [UIMenuController sharedMenuController];
    [menuController setTargetRect:recognizer.view.frame inView:recognizer.view.superview];
    [menuController setMenuVisible:YES animated:YES];
    [recognizer.view becomeFirstResponder];
}
The most important part of the codes above is:
UIMenuController *menuController = ...
Menu Controller
Menu Controller
This small bit of code instantiate a UIMenuController object that displays a menu with a long press. There are, however, limitations for labels such as the select: and selectAll: methods does not work.
You’ll notice that the menu controller is a singleton, and this is the only way to instantiate one.
To control which actions to include, you should modify the method canPerformAction:withSender: accordingly. The default implementation of this method returns YES if the responder class implements the requested action and calls the next responder if it does not. Subclasses may override this method to enable menu commands based on the current state; for example, you would enable the Copy command if there is a selection or disable the Paste command if the pasteboard did not contain data with the correct pasteboard representation type. If no responder in the responder chain returns YES, the menu command is disabled. Note that if your class returns NO for a command, another responder further up the responder chain may still return YES, enabling the command.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    return (action == @selector(copy:));
}

Pasting to UIImageView

- (void)paste:(id)sender
{
    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    if (pasteboard.image) self.image = pasteboard.image;
}
The copy: method will add the UIImageView’s image to the pasteboard via the image property while paste: will place the image from the pasteboard if it exists.

UIResponderStandardEditActions

Note: These methods are not implemented in NSObject
Standard Edit Actions
Name iOS Description
copy: 3.0 This method is invoked when the user taps the Copy command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should convert the selection into an appropriate object (if necessary) and write that object to a pasteboard.
cut: 3.0 This method is invoked when the user taps the Cut command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should convert the selection into an appropriate object (if necessary) and write that object to a pasteboard. It should also remove the selected object from the user interface and, if applicable, from the application’s data model.
paste: 3.0 This method is invoked when the user taps the Paste command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should read the data in the pasteboard, convert the data into an appropriate internal representation (if necessary), and display it in the user interface.
delete: 3.2 This method is invoked when the user taps the Delete command of the editing menu. A subclass of UIResponder typically implements this method by removing the selected object from the user interface and, if applicable, from the application’s data model. It should not write any data to the pasteboard.
select: 3.0 This method is invoked when the user taps the Select command of the editing menu. This command is used for targeted selection of items in the receiving view that can be broken up into chunks. This could be, for example, words in a text view. Another example might be a view that puts lists of visible objects in multiple groups; the select: command could be implemented to select all the items in the same group as the currently selected item.
selectAll: 3.0 This method is invoked when the user taps the Select All command of the editing menu. A subclass of UIResponder typically implements this method by selecting all objects in the current view. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
makeTextWritingDirectionLeftToRight: 5.0 A subclass of UIResponder typically implements this method by changing the current writing direction of its text editing area. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
makeTextWritingDirectionRightToLeft: 5.0 A subclass of UIResponder typically implements this method by changing the current writing direction of its text editing area. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
toggleBoldface: 6.0 A subclass of UIResponder typically implements this method by applying a boldface style to the selected text if it does not currently have it, or by removing the style if it does. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
toggleItalics: 6.0 A subclass of UIResponder typically implements this method by applying an italic style to the selected text if it does not currently have it, or by removing the style if it does. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
toggleUnderline: 6.0 A subclass of UIResponder typically implements this method by applying an underline style to the selected text if it does not currently have it, or by removing the style if it does. The command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn’t handle the command in the current context, it should pass it to the next responder.
increaseSize: 7.0 The system calls this action method in response to the user pressing Command-plus (+) on an attached hardware keyboard. Typical responses for this type of event are to increase the font size of text or to change the zoom level of scroll views.
decreaseSize: 7.0 The system calls this action method in response to the user pressing Command-minus (-) on an attached hardware keyboard. Typical responses for this type of event are to decrease the font size of text or to change the zoom level of scroll views.

Custom Menu Item

There are situations you’ll want to add custom items in the menu, you can do so by initialising a UIMenuItem object and passing it to the UIMenuController object. UIMenuController has a menuItems property, which is an NSArray of UIMenuItem objects. You can create a method such as:
ViewController.h
- (void)longPressGestureRecognizer:(UIGestureRecognizer *)recognizer
{
    UIMenuItem *removeItem = [self.label menuItemRemove];
    UIMenuController *menuController = ...
    ...
    menuController.menuItems = @[ removeItem ];
}
PasteboardLabel.{h,m}
@interface PasteboardLabel : UILabel

- (UIMenuItem *)menuItemRemove;

@end

@implementation PasteboardLabel

- (UIMenuItem *)menuItemRemove
{
    return [[UIMenuItem alloc] initWithTitle:@"Remove" action:@selector(remove:)];
}

- (void)remove:(id)sender
{
    self.text = @"";
}

@end
By the help of UIMenuItem, you can make a powerful menu designed for rich text editing and the likes. One thing to remember though with UIMenuItem, if the specified action is not implemented, that item will not appear in the menu.