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.
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.
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
action:@selector(doLongTouch)];
[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"
action:@selector(doSMS)];
[options addObject:item];
[item release];
}
[menu setMenuItems:options];
[menu setTargetRect:CGRectMake(0,0,self.frame.size.width,self.frame.size.height)
inView:self];
[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.
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];
}
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.
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"
action:@selector(copySelection:)];
[options addObject:copy];
[copy release];
I was even able to get four items!
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.
Permanent link to this post: http://xinsight.ca/blog/add-copy-funtionality-to-a-custom-uiview/
Older: Using a Repeating NSTimer in a UIViewController
Newer: Tracking keyword ranking and improving findability in the Android Marketplace