Add copy functionality to a custom UIView

24 Jun 2011

In the crowded App Store, anything you can do to make your app part of people’s online conversations will help spread the word and give your app more exposure. My latest app Cosmos Timer has a nice countdown for various events, and I wanted people to be able to share that via email or twitter.

menu with options to copy or tweet

I expected adding a popup menu with the copy feature to be painful or at least difficult, so I procrastinated adding it. But once I got around to tackling it, I was surprised by how straightforward it actually was.

Enable Long Touch

The convention for bringing up the copy menu is the long touch. With iOS 4 this is as simple as adding a gesture to your custom UIView. (Apple calls it a “long press”.)

- (id)initWithFrame:(CGRect)frame;
  // ... whatever you create for your custom view goes here ...
  UILongPressGestureRecognizer *hold;
  hold = [[UILongPressGestureRecognizer alloc] initWithTarget:self 
  [self addGestureRecognizer:hold];
  [hold release];

A long touch calls the method doLongTouch. Let’s add an option to send a text message (SMS):

- (void) doLongTouch;
  [self becomeFirstResponder];
  UIMenuController *menu = [UIMenuController sharedMenuController];

  NSMutableArray *options = [NSMutableArray array];

  if ([MFMessageComposeViewController canSendText]) {
    UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:@"SMS"
    [options addObject:item];
    [item release];
  [menu setMenuItems:options];
  [menu setTargetRect:CGRectMake(0,0,self.frame.size.width,self.frame.size.height)
  [menu setMenuVisible:YES animated:YES];

The doSMS method just pops up the standard SMS screen. (You’ll need to add the MessageUI.framework to your project.)

- (void) doSMS;
  MFMessageComposeViewController *msg = [[MFMessageComposeViewController alloc] init];
  msg.messageComposeDelegate = self;
  msg.body = @"A boring string that should be interesting.";

  [vc presentModalViewController:msg animated:YES];
  [msg release];

The “copy” option is supported by an informal protocol (see UIResponderStandardEditActions for details). Just create these methods and it will appear:

- (BOOL)canBecomeFirstResponder;
  return YES;

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
  BOOL r = NO;
  if (action == @selector(copy:)) {
    r = YES;
  } else {
    r = [super canPerformAction:action withSender:sender];
  return r;

- (void) copy:(id)sender;
  NSString *text = @"This should be something more interesting than a static string.";	
  UIPasteboard *paste = [UIPasteboard generalPasteboard];
  paste.persistent = YES;
  [paste setString:text];	

To summarize: When the long press gesture is detected, the “copy” menu option is automatically created if your UIView has a copy: method , and you can explicitly add other options to the menu.

To actually copy the text to the clipboard, you just need to assign an NSString to the UIPasteboard.

Easy. No excuse not to make your next project a little more social.

Finishing Touches

Without any visual feedback, your users won’t know that your custom UIView has any interactivity. You’ll want to add these events – hopefully with something more attractive than a red background.

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
	self.backgroundColor = [UIColor redColor];

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
	self.backgroundColor = [UIColor clearColor];

// note: cancelled called when long touch is detected
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
	self.backgroundColor = [UIColor clearColor];

One Limitation

I know I’ve seen popup menus with more than three items before, but I was only able to get two to appear before items would be moved onto a second menu which could only be seen by tapping “More…”. I found that annoying, so I limited the menu to two items.

If you happen to know of a workaround to get more than two items in the popup menu, please share how in the comments.

Pop Up Menu Split


Thanks to Jonathan Badeen for pointing out that if you rename copy: to copySelection: and then explicitly add a copy menu item, you can can avoid the “More…” problem.

// note: we call 'copySelection:' instead of 'copy:'
  UIMenuItem *copy = [[UIMenuItem alloc] initWithTitle:@"Copy"
  [options addObject:copy];
  [copy release];

I was even able to get four items!

Four Popup Menu Options

If you like this post, have a look at Cosmos Timer - it can track both short and ridiculously long events. It’s the perfect place to keep track of things that don’t easily fit into a todo list or a calendar.

Older: Using a Repeating NSTimer in a UIViewController

Newer: Tracking keyword ranking and improving findability in the Android Marketplace

View Comments

Related Posts

Recent Posts