Question

I've implemented a long press gesture recognizer on a table view cell that shows the UIMenuController. But when menu shows, the corresponding table view cell deselects. Before showing the menu, I call, as required, [self becomeFirstResponder]. I think that this call deselects the cell, but how to make it to stay selected while the UIMenuController is visible?

Was it helpful?

Solution 2

A simpler way of implementing this is using the specific UITableViewDelegate methods for dealing with UIMenuController. But first, to make the cell stay selected, store the value of the cell presenting the menu in your class:

NSIndexPath                     *_editingIndexPath;

Then implement the UITableViewDelegateMethods:

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyCustomTableViewCell *cell = (MyCustomTableViewCell *) [_tableView cellForRowAtIndexPath:indexPath];
    _editingIndexPath = indexPath;
    cell.showingMenu = YES;

    return YES;
}

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    if (action == @selector(copy:)) {
        return YES;
    }

    return NO;
}


- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    if (action == @selector(copy:))
    {
        UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
        if (cell && [cell isKindOfClass:[MessageConversationCell class]])
        {
            [UIPasteboard generalPasteboard].string = cell.textLabel.text;
        }
    }
}

The code above will take care of showing a "copy" menu on the cell after a long press. Now, if you want the cell to stay selected while the menu is displayed:

Add a @property in your custom cell named "showingMenu" (note that this property was already set in the first block of code in this answer).

@property (nonatomic, assign) BOOL showingMenu;

Add (or modified if already present) the following method to your custom cell. This will take care of keeping the cell highlighted after the menu tried to unhighlight it (you may implement your own logic for highlighting a cell, in that case put it in the first branch of the if conditional):

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{

    if (_showingMenu)
    {
        [super setHighlighted:YES]
    }
    else
    {
        [super setHighlighted:highlighted];
    }
}

Add an observer to be notified when the menu is going to be presented. This goes into the view controller, NOT in the custom cell:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didShowEditMenu:) name:UIMenuControllerDidShowMenuNotification object:nil];

Add on the view controller the method to be called when the menu is displayed:

- (void)didShowEditMenu:(NSNotification *)not {
    [_tableView selectRowAtIndexPath:_editingIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    MyCustomTableViewCell *cell = (MyCustomTableViewCell*)[_conversationTableView cellForRowAtIndexPath:_editingIndexPath];
    cell.showingMenu = NO;
}

And don't forget to remove the observer when no longer needed:

[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidShowMenuNotification object:nil];

This will show a menu when a cell is long pressed, and keep the cell selected until the menu disappears, either because an option was chosen or because the user tapped somewhere else. It works pretty much like Whatsapp works when you select a message.

OTHER TIPS

In your UITableViewDelegate, override tableView:willDeselectRowAtIndexPath: and return nil when you don’t want your row to deselect, according to the documentation.

You say you are calling [self becomeFirstResponder] but your question does not indicate which object self is. I would guess self is your controller. You should be sending the becomeFirstResponder message to the UITableViewCell that is spawning the UIMenuController.

- (void)longPress:(UILongPressGestureRecognizer *)recognizer 
{ 
    if (recognizer.state == UIGestureRecognizerStateBegan) 
    {    
        TSTableViewCell *cell = (TSTableViewCell *)recognizer.view;

        //This is your problem
        [cell becomeFirstResponder]; 

        UIMenuItem *flag = [[UIMenuItem alloc] initWithTitle:@"Flag" action:@selector(flag:)];    
        UIMenuItem *approve = [[UIMenuItem alloc] initWithTitle:@"Approve" action:@selector(approve:)]; 
        UIMenuItem *deny = [[UIMenuItem alloc] initWithTitle:@"Deny" action:@selector(deny:)];

        UIMenuController *menu = [UIMenuController sharedMenuController];    
        [menu setMenuItems:[NSArray arrayWithObjects:flag, approve, deny, nil]];    
        [menu setTargetRect:cell.frame inView:cell.superview];    
        [menu setMenuVisible:YES animated:YES];    
    }
}

I have found this solution:

- (void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
    if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {

        UITableViewCell *cell = (UITableViewCell *)longPressRecognizer.view;

        [cell setSelected:YES];

        ...        
    }
}         
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top