For those who still looking for answer here is code (main idea belongs to neon1, see linked question).
The idea is following: if a responder doesn't know how to handle given action, it propogates it to the next responder in chain. Until now we have two candidates for first responders:
- Cell
- TextField
Each of them have separate chain of responders (in fact, no, they do have common ancestor, so their chains have something in common, but we cannot use it):
UITextField <- UIView <- ... <- UIWindow <- UIApplication
UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication
So we would like to have following chain of reponders:
UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication
We need to subclass UITextField (code is taken from here):
CustomResponderTextView.h
@interface CustomResponderTextView : UITextView
@property (nonatomic, weak) UIResponder *overrideNextResponder;
@end
CustomResponderTextView.m
@implementation CustomResponderTextView
@synthesize overrideNextResponder;
- (UIResponder *)nextResponder {
if (overrideNextResponder != nil)
return overrideNextResponder;
else
return [super nextResponder];
}
@end
This code is very simple: it returns real responder in case we haven't set any custom next responder, otherwise returns our custom responder.
Now we can set new responder in our code (my example adds custom actions):
CustomCell.m
@implementation CustomCell
- (BOOL) canBecomeFirstResponder {
return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
return (action == @selector(copyMessage:) || action == @selector(deleteMessage:));
}
@end
- (void) copyMessage:(id)sender {
// copy logic here
}
- (void) deleteMessage:(id)sender {
// delete logic here
}
Controller
- (void) viewDidLoad {
...
UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"Custom copy" action:@selector(copyMessage:)];
UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Custom delete" action:@selector(deleteMessage:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuItems:@[copyItem, deleteItem]];
...
}
- (void) longCellTap {
// cell is UITableViewCell, that has received tap
if ([self.textField isFirstResponder]) {
self.messageTextView.overrideNextResponder = cell;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil];
} else {
[cell becomeFirstResponder];
}
}
- (void)menuDidHide:(NSNotification*)notification {
self.messageTextView.overrideNextResponder = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
}
Last step is making first responder (in our case text field) propogate copyMessage: and deleteMessage: actions to next responder (cell in our case). As we know iOs sends canPerformAction:withSender: to know, if given responder can handle the action.
We need to modify CustomResponderTextView.m and add the following function:
CustomResponderTextView.m
...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (overrideNextResponder != nil)
return NO;
else
return [super canPerformAction:action withSender:sender];
}
...
In case we've set our custom next responder we send all actions to it (you can modify this part, if you need some actions on textField), otherwise we ask our supertype if it can handles it.