0
votes

i am trying to use UIMenuController to perform a custom action on the table cell, from which the UIMenuController triggered by long time press.

I registered UILongPressGestureRecognizer in the viewDidLoad method in my subclass of UITableViewController and added custom item with @selector(handleMyAction).

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.refreshControl addTarget:self action:@selector(refreshView:) forControlEvents:UIControlEventValueChanged];

    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    longPressGesture.minimumPressDuration = .5;
    longPressGesture.delegate = self;
    [self.tableView addGestureRecognizer:longPressGesture];
}

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if(gestureRecognizer.state == UIGestureRecognizerStateBegan)
    {
        CGPoint point = [gestureRecognizer locationInView:self.tableView];
        NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint:point];
        if(indexPath == nil) return ;
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

        UIMenuItem *it = [[UIMenuItem alloc] initWithTitle:@"My Action on this cell" action:@selector(handleMyAction:)];
        UIMenuController *menu = [UIMenuController sharedMenuController];
        [menu setMenuItems:[NSArray arrayWithObjects:it, nil]];
        [menu setTargetRect:cell.frame inView:cell.superview];
        [menu setMenuVisible:YES animated:YES];
        [self becomeFirstResponder];
    }
}

I also override the

- (BOOL)canBecomeFirstResponder{
    return YES;
}

When I press on one cell the context menu with the custom entry displays properly. BUT, the problem is how can I implement the method to handle the custom action, which should be performed on the tapped cell.

- (void)handleMyAction:(id)sender
{
    NSLog(@"Action triggered, however need some way to refer the tapped cell");
}

Because the only information i can get in this method is the sender, which is the UIMenuController self, but i have no idea how can get the cell, on which the Menu triggered, so i can do further action regarding the cell itself.

Could some one help me on that?

Thanks. Hai

3

3 Answers

0
votes

Well you're currently adding the UIGestureRecognizer to the tableview itself. Why not add it to each cell instead (in cellForRowAtIndexPath when they are setup) ?

0
votes

Thanks valheru. I find a "nice" approach to achieve that:)

Step one: In MyTableViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    longPressGesture.minimumPressDuration = .5;
    longPressGesture.delegate = self;
    [self.view addGestureRecognizer:longPressGesture];
}

which register the gesture recognizer of the long press on the table view controller.

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

which allows MyTableViewController response the long press and popup the context menu.

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if(gestureRecognizer.state == UIGestureRecognizerStateBegan)
    {
        CGPoint point = [gestureRecognizer locationInView:self.tableView];
        NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint:point];
        if(indexPath == nil) return ;

        MyCell *cell = (MyCell *)[self.tableView cellForRowAtIndexPath:indexPath];
        UIMenuItem *determine = [[UIMenuItem alloc] initWithTitle:@"My Action on this cell" action:@selector(handleMyAction:)];
        UIMenuController *menu = [UIMenuController sharedMenuController];
        [menu setMenuItems:[NSArray arrayWithObjects:determine, nil]];
        [menu setTargetRect:cell.frame inView:cell.superview];
        [menu setMenuVisible:YES animated:YES];
        [cell becomeFirstResponder]; //here set the cell as the responder of the menu action
        cell.delegate = self;// this is optional, if you don't want to implement logic in cell class
    }
}

create the UIMenuController and popup when i long press the cell.

-(void)handleMyAction: (UITableViewCell *)cell
{
    NSLog(@"%@", cell);
}

this function will be called later from the cell which is pressed.

Step two: Create a subclass of UITableViewCell named MyCell

In MyCell.h defines the table view controller the cell belongs as the delegate of the cell. And the callback function when the menu entry clicked

@property (nonatomic, strong) MyTableViewController *delegate;
-(void)handleMyAction:(id)sender;

in MyCell.m

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if(action == @selector(handleMyAction:))
    {
        return YES;
    }
    return NO;
}

allows the MyCell to be the first responder and response the handleMyAction action by clicking on menu entry.

-(void)handleMyAction:(id)sender
{
    [self.delegate handleMyAction:self]; //it's a coincidence both functions have the same name:)
}

this is the definition of the callback function, which will be called when click on the menu entry, and it in turn call the handMyAction function in the delegate of the cell (MyTableViewController, where the logic regarding the cell could be implemented.)

0
votes

First declare a NSIndexPath type as a class variable.

@property (nonatomic, strong) NSIndexPath *savedIndexPathForThePressedCell;

Now in the Long press gesture recognizer function, get the TableViewCell using the recognizer view. Now save the IndexPath for the TableViewCell

     - (void)longPressGestureFunction:(UILongPressGestureRecognizer *)recognizer
    {
        UITableViewCell *lTableViewCell = (UITableViewCell *)recognizer.view;
        [lTableViewCell becomeFirstResponder];

        /*Save the Indexpath of the cell pressed*/
        self.savedIndexPathForThePressedCell = [mTableView indexPathForCell:lTableViewCell];

if (recognizer.state == UIGestureRecognizerStateBegan)
    {
        UIMenuItem *MenuDelete = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(Delete:)];
        UIMenuItem *MenuForward = [[UIMenuItem alloc] initWithTitle:@"Forward" action:@selector(Forward:)];
        UIMenuItem *MenuAddToContacts = [[UIMenuItem alloc] initWithTitle:@"Add To Contacts" action:@selector(addToContacts:)];

        mSharedMenu = [UIMenuController sharedMenuController];
[mSharedMenu setMenuItems:[NSArray arrayWithObjects: MenuDelete, MenuForward, nil]];

        [mSharedMenu setMenuVisible:YES animated:YES];
    }

Now in the Menu selector method, select the row based on the saved indexPath.

- (void)Delete:(id)sender {
//  NSLog(@"\n Delete Selected \n");

    [mTableView setEditing:YES animated:YES];

    [mTableView selectRowAtIndexPath:self.savedIndexPathForThePressedCell animated:YES scrollPosition:UITableViewScrollPositionNone];
}

I hope this helps!!!